This is an automated email from the ASF dual-hosted git repository.
ardovm pushed a commit to branch AOO42X
in repository https://gitbox.apache.org/repos/asf/openoffice.git
The following commit(s) were added to refs/heads/AOO42X by this push:
new 6e673e3dab Merge pull request #491 from apache/bugfix-dxf-filter
6e673e3dab is described below
commit 6e673e3dab632ab4cfbfcfca2520e9c1a871f8d8
Author: Arrigo Marchiori <[email protected]>
AuthorDate: Tue Jul 28 22:11:12 2026 +0200
Merge pull request #491 from apache/bugfix-dxf-filter
Bugfix dxf filter
(cherry pick and adaptation of merge commit
c082241a9b512a7cac07a6b4facb9ec65953be75)
---
.gitignore | 17 +
main/filter/source/graphicfilter/idxf/dxf2mtf.cxx | 553 +++++++++++++++++----
main/filter/source/graphicfilter/idxf/dxf2mtf.hxx | 7 +
main/filter/source/graphicfilter/idxf/dxfentrd.cxx | 151 +++++-
main/filter/source/graphicfilter/idxf/dxfentrd.hxx | 70 ++-
main/filter/source/graphicfilter/idxf/dxfgrprd.cxx | 14 +-
main/filter/source/graphicfilter/idxf/dxfreprd.cxx | 354 ++++++++++---
main/filter/source/graphicfilter/idxf/dxftblrd.cxx | 20 +-
main/filter/source/graphicfilter/idxf/dxfvec.cxx | 13 +-
main/filter/source/graphicfilter/idxf/idxf.cxx | 19 +-
main/filter/source/graphicfilter/idxf/makefile.mk | 2 +-
main/svtools/source/filter/filter.cxx | 28 +-
12 files changed, 1068 insertions(+), 180 deletions(-)
diff --git a/.gitignore b/.gitignore
index 84c7c4c422..d15366c980 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,3 +86,20 @@ main/winaccessibility/source/UAccCOMIDL/dlldata.c
# RepoRevision hash file
/main/solenv/inc/reporevision.lst
+
+# Python
+.venv
+
+# Visual Studio Code
+.vscode/
+
+# JetBrains IDEs
+.idea/
+
+# claude
+.claude/
+
+# bazel
+bazel-*
+user.bazelrc
+*.lock
diff --git a/main/filter/source/graphicfilter/idxf/dxf2mtf.cxx
b/main/filter/source/graphicfilter/idxf/dxf2mtf.cxx
index 7f2706117d..ac27b0a3cb 100644
--- a/main/filter/source/graphicfilter/idxf/dxf2mtf.cxx
+++ b/main/filter/source/graphicfilter/idxf/dxf2mtf.cxx
@@ -27,6 +27,7 @@
#include <string.h>
#include <vcl/gdimtf.hxx>
#include <vcl/virdev.hxx>
+#include <vcl/hatch.hxx>
#include <tools/poly.hxx>
#include "dxf2mtf.hxx"
@@ -132,15 +133,23 @@ DXFLineInfo DXF2GDIMetaFile::LTypeToDXFLineInfo(const
char * sLineType)
}
}
-#if 0
- if (aDXFLineInfo.DashCount > 0 && aDXFLineInfo.DashLen == 0.0)
- aDXFLineInfo.DashLen ( 1 );
- if (aDXFLineInfo.DotCount > 0 && aDXFLineInfo.DotLen() == 0.0)
- aDXFLineInfo.SetDotLen( 1 );
- if (aDXFLineInfo.GetDashCount > 0 || aDXFLineInfo.GetDotCount > 0)
- if (aDXFLineInfo.GetDistance() == 0)
- aDXFLineInfo.SetDistance( 1 );
-#endif
+ // A DXF pattern element of exactly 0.0 is a *dot* (a point), distinct
from a
+ // positive-length dash (e.g. DOT2 = 0,-3.175). AutoCAD paints it as a
small
+ // dot, but VCL has no round dot: LineInfo strokes every "on" run as a
short
+ // segment, so a dot is just a very short dash. Rendered verbatim the
length
+ // collapses to a sub-unit mark (Transform() only clamps it to 1/100mm)
and the
+ // line came out invisible (issue 70275). Give a zero-length dot/dash a
small
+ // model-unit length so it survives scaling; keep it a SMALL fraction
of the
+ // gap (0.05) so it reads as a dot, not a dash. Done here in model
units, not
+ // after scaling, so it tracks the drawing size. Only true 0.0 elements
are
+ // adjusted — real authored dashes keep their length.
+ const double fDotOnFraction = 0.05; // dot mark length as a fraction
of the gap
+ if (aDXFLineInfo.fDistance != 0.0) {
+ if (aDXFLineInfo.nDotCount > 0 && aDXFLineInfo.fDotLen == 0.0)
+ aDXFLineInfo.fDotLen = aDXFLineInfo.fDistance *
fDotOnFraction;
+ if (aDXFLineInfo.nDashCount > 0 && aDXFLineInfo.fDashLen == 0.0)
+ aDXFLineInfo.fDashLen = aDXFLineInfo.fDistance *
fDotOnFraction;
+ }
return aDXFLineInfo;
}
@@ -475,6 +484,104 @@ void DXF2GDIMetaFile::DrawTextEntity(const DXFTextEntity
& rE, const DXFTransfor
}
+// Draw a TEXT entity projected into the (3D) view, used when the drawing is
+// rendered through a non-top viewport. VCL's DrawText can only place text
with a
+// position + uniform height + single rotation, so it cannot follow the shear /
+// foreshortening a 3D projection produces. Instead we take the glyph OUTLINES
and
+// push them through the full transform like any other geometry, so the text
lies
+// in its projected plane. The flat 2D path (DrawTextEntity) is left untouched.
+void DXF2GDIMetaFile::Draw3DTextEntity(const DXFTextEntity & rE, const
DXFTransform & rTransform)
+{
+ ByteString aStr( rE.sText );
+ String aUString( aStr, pDXF->getTextEncoding() );
+ if ( aUString.Len()==0 ) return;
+
+ long nColor = GetEntityColor(rE);
+ if ( nColor<0 ) return;
+ Color aColor = ConvertColor((sal_uInt8)nColor);
+
+ // Extract the glyph outlines from a SCRATCH device, never from
pVirDev: the
+ // recording device has output disabled, so its GetTextOutlines falls
back to a
+ // bitmap path that fails ("could not create system bitmap"). A clean
scalable
+ // font on a plain VirtualDevice takes the vector path. The nominal
height is
+ // arbitrary — the extent is self-calibrated below.
+ const long nNominal = 1000;
+ Font aFont;
+ aFont.SetFamily(FAMILY_SWISS);
+ aFont.SetSize(Size(0,nNominal));
+ aFont.SetAlign(ALIGN_BASELINE);
+ VirtualDevice aTextDev;
+ aTextDev.SetFont(aFont);
+ PolyPolyVector aGlyphs;
+ sal_Bool bOK = aTextDev.GetTextOutlines( aGlyphs, aUString );
+ if ( !bOK || aGlyphs.empty() ) {
+ DrawTextEntity(rE,rTransform); // no outlines available ->
flat fallback
+ return;
+ }
+
+ // GetTextOutlines returns the glyphs in an opaque internal scale (it
toggles
+ // the map mode and rescales by a pixel factor), so we can't assume the
height
+ // is nNominal. Self-calibrate: measure the real outline extent and
normalise by
+ // its height, so one text-height maps to 1.0 regardless of the device
scale.
+ double fMinY=0.0, fMaxY=0.0;
+ sal_Bool bFirst = sal_True;
+ sal_uInt32 g;
+ for ( g=0; g<aGlyphs.size(); g++ ) {
+ const PolyPolygon & rGlyph = aGlyphs[ g ];
+ sal_uInt16 c;
+ for ( c=0; c<rGlyph.Count(); c++ ) {
+ const Polygon & rContour = rGlyph[ c ];
+ sal_uInt16 i, nPts = rContour.GetSize();
+ for ( i=0; i<nPts; i++ ) {
+ const double y = (double)rContour[ i ].Y();
+ if ( bFirst ) { fMinY=fMaxY=y;
bFirst=sal_False; }
+ else { if (y<fMinY) fMinY=y; if (y>fMaxY)
fMaxY=y; }
+ }
+ }
+ }
+ const double fHeight = fMaxY - fMinY;
+ if ( fHeight <= 0.0 ) { // degenerate outlines -> flat fallback
+ DrawTextEntity(rE,rTransform);
+ return;
+ }
+ const double fInv = 1.0/fHeight;
+
+ // Text-local coords (baseline at Y=0, one text-height == 1.0, Y up) ->
DXF
+ // text placement (width/height/rotation/insertion) -> the incoming
extrusion+
+ // view transform. This is the same chain the renderer uses for
geometry.
+ // The outlines are normalised to text-height units in BOTH axes, so X
and Y
+ // must both scale by fHeight; fXScale (DXF group 41 width factor) is
only an
+ // extra horizontal stretch on top (default 1.0). Scaling X by fXScale
alone —
+ // as DrawTextEntity can, because it re-derives width from the font —
would
+ // collapse the glyphs to a vertical sliver here.
+ const double fWScale = rE.fHeight * ( rE.fXScale>0.0 ? rE.fXScale : 1.0
);
+ DXFTransform aT(
DXFTransform(fWScale,rE.fHeight,1.0,rE.fRotAngle,rE.aP0), rTransform );
+
+ if ( aActFillColor!=aColor ) pVirDev->SetFillColor( aActFillColor =
aColor );
+ if ( aActLineColor!=aColor ) pVirDev->SetLineColor( aActLineColor =
aColor );
+
+ for ( g=0; g<aGlyphs.size(); g++ ) {
+ const PolyPolygon & rGlyph = aGlyphs[ g ];
+ PolyPolygon aDevGlyph;
+ sal_uInt16 c;
+ for ( c=0; c<rGlyph.Count(); c++ ) {
+ const Polygon & rContour = rGlyph[ c ];
+ sal_uInt16 nPts = rContour.GetSize();
+ Polygon aDev( nPts );
+ sal_uInt16 i;
+ for ( i=0; i<nPts; i++ ) {
+ const Point & rP = rContour[ i ];
+ aT.Transform( DXFVector( (double)rP.X()*fInv,
+
-(double)rP.Y()*fInv, 0.0 ),
+ aDev[ i ] );
+ }
+ aDevGlyph.Insert( aDev );
+ }
+ pVirDev->DrawPolyPolygon( aDevGlyph );
+ }
+}
+
+
void DXF2GDIMetaFile::DrawInsertEntity(const DXFInsertEntity & rE, const
DXFTransform & rTransform)
{
const DXFBlock * pB;
@@ -586,126 +693,331 @@ void DXF2GDIMetaFile::DrawPolyLineEntity(const
DXFPolyLineEntity & rE, const DXF
void DXF2GDIMetaFile::DrawLWPolyLineEntity(const DXFLWPolyLineEntity & rE,
const DXFTransform & rTransform )
{
- sal_Int32 i, nPolySize = rE.nCount;
- if ( nPolySize && rE.pP )
+ const sal_Int32 nVtxCount = rE.nCount;
+ if ( nVtxCount <= 0 || rE.pP == NULL )
+ return;
+
+ double fW = rE.fConstantWidth;
+ if ( !SetLineAttribute( rE, rTransform.TransLineWidth( fW ) ) )
+ return;
+
+ const sal_Bool bClosed = ( rE.nFlags & 1 ) != 0;
+
+ // Build the outline in device space, expanding any segment that
carries a
+ // non-zero bulge into an arc. In DXF a bulge is tan(sweep/4), so bulge
1.0
+ // is a 180 degree arc; a circle is commonly stored as two bulged
segments
+ // (e.g. Eagle CAD). Straight segments (bulge 0) contribute just the
vertex.
+ DXFPointArray aPtAry;
+ Point aPt;
+ for ( sal_Int32 i = 0; i < nVtxCount; i++ )
{
- Polygon aPoly( (sal_uInt16)nPolySize);
- for ( i = 0; i < nPolySize; i++ )
+ rTransform.Transform( rE.pP[ i ], aPt );
+ aPtAry.push_back( aPt );
+
+ sal_Int32 iNext = i + 1;
+ if ( iNext >= nVtxCount )
{
- rTransform.Transform( rE.pP[ (sal_uInt16)i ], aPoly[
(sal_uInt16)i ] );
+ if ( !bClosed ) break; // open: no segment after the
last vertex
+ iNext = 0; // closed: last vertex arcs
back to the first
}
- double fW = rE.fConstantWidth;
- if ( SetLineAttribute( rE, rTransform.TransLineWidth( fW ) ) )
+
+ const double fBulge = ( rE.pBulge != NULL ) ? rE.pBulge[ i ] :
0.0;
+ if ( fBulge != 0.0 )
{
- if ( ( rE.nFlags & 1 ) != 0 )
- pVirDev->DrawPolygon( aPoly );
- else
- pVirDev->DrawPolyLine( aPoly );
- // ####
- //pVirDev->DrawPolyLine( aPoly, aDXFLineInfo );
+ const DXFVector & rS = rE.pP[ i ];
+ const DXFVector & rEnd = rE.pP[ iNext ];
+ // Arc centre from the bulge (cotangent form): the
centre lies on the
+ // chord's perpendicular bisector, offset by
cot(sweep/2)/2 * chord.
+ const double fCot = 0.5 * ( 1.0 / fBulge - fBulge );
+ const double fCx = 0.5 * ( ( rS.fx + rEnd.fx ) + fCot *
( rS.fy - rEnd.fy ) );
+ const double fCy = 0.5 * ( ( rS.fy + rEnd.fy ) + fCot *
( rEnd.fx - rS.fx ) );
+ const double fRx = rS.fx - fCx;
+ const double fRy = rS.fy - fCy;
+ const double fRadius = sqrt( fRx * fRx + fRy * fRy );
+ const double fStartAng = atan2( fRy, fRx );
+ const double fSweep = 4.0 * atan( fBulge ); //
signed: >0 = CCW
+ sal_Int32 nArcPoints = (sal_Int32)( fabs( fSweep ) / (
2 * 3.14159265359 )
+
* (double)OptPointsPerCircle + 0.5 );
+ // Emit the interior arc samples; the start vertex is
already pushed
+ // and the end vertex is pushed as the next loop's
leading vertex.
+ for ( sal_Int32 k = 1; k < nArcPoints; k++ )
+ {
+ const double fAng = fStartAng + fSweep *
(double)k / (double)nArcPoints;
+ rTransform.Transform(
+ DXFVector( fCx + fRadius * cos( fAng ),
+ fCy + fRadius * sin(
fAng ),
+ rS.fz ),
+ aPt );
+ aPtAry.push_back( aPt );
+ }
}
}
+
+ const sal_Int32 nPolySize = (sal_Int32)aPtAry.size();
+ if ( nPolySize < 2 )
+ return;
+
+ if ( nPolySize <= 0xFFFF )
+ {
+ // Fits in a single tools Polygon (which is indexed by
sal_uInt16).
+ Polygon aPoly( (sal_uInt16)nPolySize );
+ for ( sal_Int32 i = 0; i < nPolySize; i++ )
+ aPoly[ (sal_uInt16)i ] = aPtAry[ i ];
+ if ( bClosed )
+ pVirDev->DrawPolygon( aPoly );
+ else
+ pVirDev->DrawPolyLine( aPoly );
+ return;
+ }
+
+ // The tessellated outline can exceed the 0xFFFF points a tools Polygon
holds.
+ // Render it as a sequence of Polygon segments that overlap by one
point, so
+ // the whole line is drawn without truncation and the stroke stays
continuous.
+ const sal_Int32 nMaxChunk = 0xFFFF;
+ sal_Int32 nStart = 0;
+ while ( nStart + 1 < nPolySize )
+ {
+ sal_Int32 nEnd = nStart + nMaxChunk;
+ if ( nEnd > nPolySize )
+ nEnd = nPolySize;
+ const sal_uInt16 nChunk = (sal_uInt16)( nEnd - nStart );
+ Polygon aChunk( nChunk );
+ for ( sal_uInt16 j = 0; j < nChunk; j++ )
+ aChunk[ j ] = aPtAry[ nStart + j ];
+ pVirDev->DrawPolyLine( aChunk );
+ nStart = nEnd - 1; // next segment shares this segment's last
point
+ }
+ if ( bClosed )
+ {
+ // Close the outline: last point back to the first.
+ Polygon aClose( 2 );
+ aClose[ 0 ] = aPtAry[ nPolySize - 1 ];
+ aClose[ 1 ] = aPtAry[ 0 ];
+ pVirDev->DrawPolyLine( aClose );
+ }
}
void DXF2GDIMetaFile::DrawHatchEntity(const DXFHatchEntity & rE, const
DXFTransform & rTransform )
{
- if ( rE.nBoundaryPathCount )
+ if ( !rE.nBoundaryPathCount )
+ return;
+
+ // Build the boundary as a PolyPolygon, tessellating curved edges so the
+ // area is measured correctly for both the solid fill and the pattern
clip.
+ sal_Int32 j;
+ PolyPolygon aPolyPoly;
+ for ( j = 0; j < rE.nBoundaryPathCount; j++ )
{
- SetAreaAttribute( rE );
- sal_Int32 j = 0;
- PolyPolygon aPolyPoly;
- for ( j = 0; j < rE.nBoundaryPathCount; j++ )
+ DXFPointArray aPtAry;
+ const DXFBoundaryPathData& rPathData = rE.pBoundaryPathData[ j
];
+ if ( rPathData.bIsPolyLine )
{
- DXFPointArray aPtAry;
- const DXFBoundaryPathData& rPathData =
rE.pBoundaryPathData[ j ];
- if ( rPathData.bIsPolyLine )
+ sal_Int32 i;
+ for( i = 0; i < rPathData.nPointCount; i++ )
{
- sal_Int32 i;
- for( i = 0; i < rPathData.nPointCount; i++ )
- {
- Point aPt;
- rTransform.Transform( rPathData.pP[ i
], aPt );
- aPtAry.push_back( aPt );
- }
+ Point aPt;
+ rTransform.Transform( rPathData.pP[ i ], aPt );
+ aPtAry.push_back( aPt );
}
- else
+ }
+ else
+ {
+ sal_uInt32 i;
+ for ( i = 0; i < rPathData.aEdges.size(); i++ )
{
- sal_uInt32 i;
- for ( i = 0; i < rPathData.aEdges.size(); i++ )
+ const DXFEdgeType* pEdge = rPathData.aEdges[ i
];
+ switch( pEdge->nEdgeType )
{
- const DXFEdgeType* pEdge =
rPathData.aEdges[ i ];
- switch( pEdge->nEdgeType )
+ case 1 :
{
- case 1 :
+ Point aPt;
+ rTransform.Transform(
((DXFEdgeTypeLine*)pEdge)->aStartPoint, aPt );
+ aPtAry.push_back( aPt );
+ rTransform.Transform(
((DXFEdgeTypeLine*)pEdge)->aEndPoint, aPt );
+ aPtAry.push_back( aPt );
+ }
+ break;
+ case 2 :
+ {
+ // Circular arc edge: sample
the sweep and push the points.
+ // Angles are in degrees;
direction follows the CCW flag.
+ const DXFEdgeTypeCircularArc*
pArc = (const DXFEdgeTypeCircularArc*)pEdge;
+ double fSweep = pArc->fEndAngle
- pArc->fStartAngle;
+ if (
pArc->nIsCounterClockwiseFlag )
+ { while ( fSweep <= 0.0
) fSweep += 360.0; }
+ else
+ { while ( fSweep >= 0.0
) fSweep -= 360.0; }
+ sal_uInt16 nPts = (sal_uInt16)(
fabs(fSweep)/360.0*(double)OptPointsPerCircle + 0.5 );
+ if ( nPts < 2 ) nPts = 2;
+ for ( sal_uInt16 k = 0; k <
nPts; k++ )
{
+ double a = (
pArc->fStartAngle + fSweep*(double)k/(double)(nPts-1) )
+
* (3.14159265359/180.0);
Point aPt;
- rTransform.Transform(
((DXFEdgeTypeLine*)pEdge)->aStartPoint, aPt );
- aPtAry.push_back( aPt );
- rTransform.Transform(
((DXFEdgeTypeLine*)pEdge)->aEndPoint, aPt );
+ rTransform.Transform(
+ DXFVector(
pArc->aCenter.fx + pArc->fRadius*cos(a),
+
pArc->aCenter.fy + pArc->fRadius*sin(a), 0.0 ), aPt );
aPtAry.push_back( aPt );
}
- break;
- case 2 :
- {
-/*
- double
frx,fry,fA1,fdA,fAng;
- sal_uInt16 nPoints,i;
- DXFVector aC;
- Point aPS,aPE;
-
fA1=((DXFEdgeTypeCircularArc*)pEdge)->fStartAngle;
-
fdA=((DXFEdgeTypeCircularArc*)pEdge)->fEndAngle - fA1;
- while ( fdA >= 360.0 )
- fdA -= 360.0;
- while ( fdA <= 0 )
- fdA += 360.0;
-
rTransform.Transform(((DXFEdgeTypeCircularArc*)pEdge)->aCenter, aC);
- if ( fdA > 5.0 &&
rTransform.TransCircleToEllipse(((DXFEdgeTypeCircularArc*)pEdge)->fRadius,frx,fry
) == sal_True )
- {
- DXFVector
aVS(cos(fA1/180.0*3.14159265359),sin(fA1/180.0*3.14159265359),0.0);
-
aVS*=((DXFEdgeTypeCircularArc*)pEdge)->fRadius;
-
aVS+=((DXFEdgeTypeCircularArc*)pEdge)->aCenter;
- DXFVector
aVE(cos((fA1+fdA)/180.0*3.14159265359),sin((fA1+fdA)/180.0*3.14159265359),0.0);
-
aVE*=((DXFEdgeTypeCircularArc*)pEdge)->fRadius;
-
aVE+=((DXFEdgeTypeCircularArc*)pEdge)->aCenter;
- if (
rTransform.Mirror() == sal_True )
- {
-
rTransform.Transform(aVS,aPS);
-
rTransform.Transform(aVE,aPE);
- }
- else
- {
-
rTransform.Transform(aVS,aPE);
-
rTransform.Transform(aVE,aPS);
- }
-
pVirDev->DrawArc(
-
Rectangle((long)(aC.fx-frx+0.5),(long)(aC.fy-fry+0.5),
-
(long)(aC.fx+frx+0.5),(long)(aC.fy+fry+0.5)),
- aPS,aPE
- );
- }
-*/
- }
- break;
- case 3 :
- case 4 :
- break;
}
+ break;
+ case 3 : // elliptical arc: not
tessellated (not seen in
+ case 4 : // practice here);
spline edges store no control
+ break; // points, so both fall
through as before.
}
}
- sal_uInt16 i, nSize = (sal_uInt16)aPtAry.size();
- if ( nSize )
- {
- Polygon aPoly( nSize );
- for ( i = 0; i < nSize; i++ )
- aPoly[ i ] = aPtAry[ i ];
- aPolyPoly.Insert( aPoly, POLYPOLY_APPEND );
- }
}
- if ( aPolyPoly.Count() )
+ sal_uInt16 i, nSize = (sal_uInt16)aPtAry.size();
+ if ( nSize )
+ {
+ Polygon aPoly( nSize );
+ for ( i = 0; i < nSize; i++ )
+ aPoly[ i ] = aPtAry[ i ];
+ aPolyPoly.Insert( aPoly, POLYPOLY_APPEND );
+ }
+ }
+ if ( !aPolyPoly.Count() )
+ return;
+
+ if ( rE.nFlags & 1 )
+ {
+ // Solid fill (group 70 bit 0): flood-fill the boundary in the
entity colour.
+ SetAreaAttribute( rE );
+ pVirDev->DrawPolyPolygon( aPolyPoly );
+ }
+ else if ( rE.bHasPatternLine )
+ {
+ // Pattern fill: render the section lines with VCL's DrawHatch,
which
+ // clips a line family to the boundary and records a
MetaHatchAction.
+ // The DXF pattern is collapsed to a single/double/triple
Hatch; the
+ // on-page angle and spacing come from mapping the
pattern-definition
+ // line's direction and inter-line offset through rTransform,
so any
+ // scale / rotation / block-insert nesting is accounted for.
+ long nColor = GetEntityColor( rE );
+ if ( nColor < 0 )
+ return;
+ Color aColor = ConvertColor( (sal_uInt8)nColor );
+
+ const double fPi = 3.14159265359;
+ double fA = rE.fPatternLineAngle * (fPi/180.0);
+ DXFVector aDir, aOff;
+ rTransform.TransDir( DXFVector( cos(fA), sin(fA), 0.0 ), aDir );
+ rTransform.TransDir( DXFVector( rE.fPatternLineOffsetX,
rE.fPatternLineOffsetY, 0.0 ), aOff );
+
+ double fDirLen = sqrt( aDir.fx*aDir.fx + aDir.fy*aDir.fy );
+ if ( fDirLen < 1e-9 )
+ {
+ // degenerate direction: fall back to a bare outline
(no blob).
+ SetLineAttribute( rE );
pVirDev->DrawPolyPolygon( aPolyPoly );
+ return;
+ }
+
+ // Perpendicular spacing between adjacent lines in device
space, and the
+ // device line angle. VCL's angle is measured in the recorded
coordinate
+ // system with its positive direction as (cos,-sin), hence
-aDir.fy.
+ double fDist = fabs( aOff.fx*aDir.fy - aOff.fy*aDir.fx ) /
fDirLen;
+ long nDistance = (long)( fDist + 0.5 );
+ if ( nDistance < 1 ) nDistance = 1;
+
+ double fAngle = atan2( -aDir.fy, aDir.fx ) * 1800.0 / fPi;
+ long nAngle10 = (long)( fAngle + (fAngle < 0.0 ? -0.5 : 0.5) );
+ nAngle10 %= 1800;
+ if ( nAngle10 < 0 )
+ nAngle10 += 1800;
+
+ HatchStyle eStyle = HATCH_SINGLE;
+ if ( rE.nHatchPatternDefinitionLines == 2 )
+ eStyle = HATCH_DOUBLE;
+ else if ( rE.nHatchPatternDefinitionLines >= 3 )
+ eStyle = HATCH_TRIPLE;
+
+ Hatch aHatch( eStyle, aColor, nDistance, (sal_uInt16)nAngle10 );
+ pVirDev->DrawHatch( aPolyPoly, aHatch );
+ }
+ else
+ {
+ // Pattern fill but no pattern-definition line was parsed:
stroke the
+ // boundary outline only (transparent fill), never a solid blob.
+ SetLineAttribute( rE );
+ pVirDev->DrawPolyPolygon( aPolyPoly );
+ }
+}
+
+void DXF2GDIMetaFile::DrawEllipseEntity(const DXFEllipseEntity & rE, const
DXFTransform & rTransform)
+{
+ if (SetLineAttribute(rE)==sal_False) return;
+
+ // Major axis vector (relative to centre) and the minor axis, which for
the
+ // common planar case (extrusion 0,0,1) is ratio*(normal x major) =
+ // ratio*(-major.y, major.x, 0). A non-default extrusion is applied
through
+ // rTransform by DrawEntities, so compute in the entity's own frame.
+ const DXFVector & aU = rE.aP1;
+ DXFVector aV(-aU.fy * rE.fRatio, aU.fx * rE.fRatio, 0.0);
+
+ double fSweep = rE.fEnd - rE.fStart;
+ while (fSweep <= 0.0) fSweep += 2*3.14159265359;
+ while (fSweep > 2*3.14159265359) fSweep -= 2*3.14159265359;
+
+ sal_uInt16 nPoints = (sal_uInt16)(
fSweep/(2*3.14159265359)*(double)OptPointsPerCircle + 0.5 );
+ if (nPoints<2) nPoints=2;
+
+ Polygon aPoly(nPoints);
+ for (sal_uInt16 i=0; i<nPoints; i++) {
+ double t = rE.fStart + fSweep*(double)i/(double)(nPoints-1);
+ rTransform.Transform( rE.aP0 + aU*cos(t) + aV*sin(t), aPoly[i]
);
}
+ pVirDev->DrawPolyLine(aPoly);
}
+
+void DXF2GDIMetaFile::DrawSplineEntity(const DXFSplineEntity & rE, const
DXFTransform & rTransform)
+{
+ // Non-uniform B-spline, evaluated with de Boor's algorithm and drawn
as a
+ // polyline. Rational (weighted) splines are treated as non-rational
(the
+ // weights are ignored) — sufficient for the planar curves this targets.
+ if ( rE.pControlPts==NULL || rE.pfKnots==NULL ) return;
+ const long p = rE.nDegree;
+ const long n = rE.nCtrlCount - 1;
+ if ( p < 1 || n < p ) return;
+ if ( rE.nKnotCount != rE.nCtrlCount + p + 1 ) return; // not a
clamped B-spline
+ if ( SetLineAttribute(rE)==sal_False ) return;
+
+ const double * U = rE.pfKnots;
+ const double u0 = U[p];
+ const double u1 = U[n+1];
+ if ( u1 <= u0 ) return;
+
+ long nPoints = rE.nCtrlCount * 8;
+ if (nPoints < 2) nPoints = 2;
+ if (nPoints > 2000) nPoints = 2000;
+
+ Polygon aPoly((sal_uInt16)nPoints);
+ DXFVector * d = new DXFVector[p+1];
+ for (long i=0; i<nPoints; i++) {
+ double u = u0 + (u1-u0)*(double)i/(double)(nPoints-1);
+ if (u >= u1) u = u1 - (u1-u0)*1e-9; // stay inside the last
knot span
+
+ // knot span k with U[k] <= u < U[k+1], clamped to [p, n]
+ long k = p;
+ while (k < n && u >= U[k+1]) k++;
+
+ long j, r;
+ for (j=0; j<=p; j++) d[j] = rE.pControlPts[k-p+j];
+ for (r=1; r<=p; r++) {
+ for (j=p; j>=r; j--) {
+ double fDenom = U[k+1+j-r] - U[k-p+j];
+ double a = (fDenom!=0.0) ? (u -
U[k-p+j])/fDenom : 0.0;
+ d[j] = d[j-1]*(1.0-a) + d[j]*a;
+ }
+ }
+ rTransform.Transform(d[p], aPoly[(sal_uInt16)i]);
+ }
+ delete[] d;
+ pVirDev->DrawPolyLine(aPoly);
+}
+
+
void DXF2GDIMetaFile::Draw3DFaceEntity(const DXF3DFaceEntity & rE, const
DXFTransform & rTransform)
{
sal_uInt16 nN,i;
@@ -773,7 +1085,7 @@ void DXF2GDIMetaFile::DrawEntities(const DXFEntities &
rEntities,
while (pE!=NULL && bStatus==sal_True) {
if (pE->nSpace==0) {
- if (pE->aExtrusion.fz==1.0) {
+ if (pE->aExtrusion.fz==1.0 || DXFCoordsAreWCS(*pE)) {
pT=&rTransform;
}
else {
@@ -800,7 +1112,10 @@ void DXF2GDIMetaFile::DrawEntities(const DXFEntities &
rEntities,
DrawSolidEntity((DXFSolidEntity&)*pE,*pT);
break;
case DXF_TEXT:
- DrawTextEntity((DXFTextEntity&)*pE,*pT);
+ if (b3DText)
+
Draw3DTextEntity((DXFTextEntity&)*pE,*pT);
+ else
+ DrawTextEntity((DXFTextEntity&)*pE,*pT);
break;
case DXF_INSERT:
DrawInsertEntity((DXFInsertEntity&)*pE,*pT);
@@ -817,6 +1132,12 @@ void DXF2GDIMetaFile::DrawEntities(const DXFEntities &
rEntities,
case DXF_HATCH :
DrawHatchEntity((DXFHatchEntity&)*pE, *pT);
break;
+ case DXF_ELLIPSE:
+ DrawEllipseEntity((DXFEllipseEntity&)*pE,*pT);
+ break;
+ case DXF_SPLINE:
+ DrawSplineEntity((DXFSplineEntity&)*pE,*pT);
+ break;
case DXF_3DFACE:
Draw3DFaceEntity((DXF3DFaceEntity&)*pE,*pT);
break;
@@ -900,6 +1221,9 @@ sal_Bool DXF2GDIMetaFile::Convert(const DXFRepresentation
& rDXF, GDIMetaFile &
if (pVPort->aDirection.fx==0 && pVPort->aDirection.fy==0)
pVPort=NULL;
}
+ // A non-top viewport means the drawing is projected in 3D; text then
has to be
+ // laid into that projection (Draw3DTextEntity) rather than drawn flat.
+ b3DText = (pVPort!=NULL) ? sal_True : sal_False;
if (pVPort==NULL) {
if (pDXF->aBoundingBox.bEmpty==sal_True)
@@ -912,21 +1236,32 @@ sal_Bool DXF2GDIMetaFile::Convert(const
DXFRepresentation & rDXF, GDIMetaFile &
fScale = 0; // -Wall added this...
}
else {
+ // Leave a small margin around the drawing. The
larger extent is
+ // scaled to fill the 10000-unit target
exactly, so geometry that
+ // lies right on the extent (e.g. a drawing
frame's outer line)
+ // would otherwise sit precisely on the fit
boundary and be
+ // clipped. This surfaced once issue 58347 made
the bounding box
+ // a tight fit to the geometry; the margin
keeps every edge inside
+ // the fitted area (symmetric, so all four
sides get breathing room).
+ const double fMargin =
+ (fWidth>fHeight ? fWidth : fHeight) *
0.01;
+ const double fFitWidth = fWidth + 2.0*fMargin;
+ const double fFitHeight = fHeight + 2.0*fMargin;
// if (fWidth<500.0 || fHeight<500.0 ||
fWidth>32767.0 || fHeight>32767.0) {
- if (fWidth>fHeight)
- fScale=10000.0/fWidth;
+ if (fFitWidth>fFitHeight)
+ fScale=10000.0/fFitWidth;
else
- fScale=10000.0/fHeight;
+ fScale=10000.0/fFitHeight;
// }
// else
// fScale=1.0;
aTransform=DXFTransform(fScale,-fScale,fScale,
-
DXFVector(-pDXF->aBoundingBox.fMinX*fScale,
-
pDXF->aBoundingBox.fMaxY*fScale,
+
DXFVector((fMargin-pDXF->aBoundingBox.fMinX)*fScale,
+
(pDXF->aBoundingBox.fMaxY+fMargin)*fScale,
-pDXF->aBoundingBox.fMinZ*fScale));
+ aPrefSize.Width() =(long)(fFitWidth*fScale+1.5);
+
aPrefSize.Height()=(long)(fFitHeight*fScale+1.5);
}
- aPrefSize.Width() =(long)(fWidth*fScale+1.5);
- aPrefSize.Height()=(long)(fHeight*fScale+1.5);
}
}
else {
diff --git a/main/filter/source/graphicfilter/idxf/dxf2mtf.hxx
b/main/filter/source/graphicfilter/idxf/dxf2mtf.hxx
index a2578e6aed..3b864db623 100644
--- a/main/filter/source/graphicfilter/idxf/dxf2mtf.hxx
+++ b/main/filter/source/graphicfilter/idxf/dxf2mtf.hxx
@@ -57,6 +57,7 @@ private:
Color aActLineColor;
Color aActFillColor;
Font aActFont;
+ sal_Bool b3DText; // draw TEXT projected into the
3D view (Draw3DTextEntity)
sal_uLong CountEntities(const DXFEntities & rEntities);
@@ -91,6 +92,8 @@ private:
void DrawTextEntity(const DXFTextEntity & rE, const DXFTransform &
rTransform);
+ void Draw3DTextEntity(const DXFTextEntity & rE, const DXFTransform &
rTransform);
+
void DrawInsertEntity(const DXFInsertEntity & rE, const DXFTransform &
rTransform);
void DrawAttribEntity(const DXFAttribEntity & rE, const DXFTransform &
rTransform);
@@ -105,6 +108,10 @@ private:
void DrawHatchEntity( const DXFHatchEntity & rE, const DXFTransform &
rTransform );
+ void DrawEllipseEntity( const DXFEllipseEntity & rE, const DXFTransform
& rTransform );
+
+ void DrawSplineEntity( const DXFSplineEntity & rE, const DXFTransform &
rTransform );
+
void DrawEntities(const DXFEntities & rEntities,
const DXFTransform & rTransform,
sal_Bool bTopEntities);
diff --git a/main/filter/source/graphicfilter/idxf/dxfentrd.cxx
b/main/filter/source/graphicfilter/idxf/dxfentrd.cxx
index e7cce8cc12..320b96c0f0 100644
--- a/main/filter/source/graphicfilter/idxf/dxfentrd.cxx
+++ b/main/filter/source/graphicfilter/idxf/dxfentrd.cxx
@@ -422,7 +422,8 @@ DXFLWPolyLineEntity::DXFLWPolyLineEntity() :
fConstantWidth( 0.0 ),
fStartWidth( 0.0 ),
fEndWidth( 0.0 ),
- pP( NULL )
+ pP( NULL ),
+ pBulge( NULL )
{
}
@@ -438,6 +439,9 @@ void DXFLWPolyLineEntity::EvaluateGroup( DXFGroupReader &
rDGR )
try
{
pP = new DXFVector[ nCount ];
+ pBulge = new double[ nCount ];
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ pBulge[ i ] = 0.0;
}
catch (::std::bad_alloc)
{
@@ -464,6 +468,14 @@ void DXFLWPolyLineEntity::EvaluateGroup( DXFGroupReader &
rDGR )
pP[ nIndex++ ].fy = rDGR.GetF();
}
break;
+ case 42:
+ {
+ // per-vertex bulge; follows the 10/20 pair of the
vertex it
+ // belongs to (nIndex has already advanced past that
vertex).
+ if ( pBulge && ( nIndex > 0 ) && ( nIndex <= nCount ) )
+ pBulge[ nIndex - 1 ] = rDGR.GetF();
+ }
+ break;
default: DXFBasicEntity::EvaluateGroup(rDGR);
}
}
@@ -471,6 +483,7 @@ void DXFLWPolyLineEntity::EvaluateGroup( DXFGroupReader &
rDGR )
DXFLWPolyLineEntity::~DXFLWPolyLineEntity()
{
delete[] pP;
+ delete[] pBulge;
}
//--------------------------DXFHatchEntity-------------------------------------
@@ -684,6 +697,7 @@ DXFHatchEntity::DXFHatchEntity() :
DXFBasicEntity( DXF_HATCH ),
bIsInBoundaryPathContext( sal_False ),
nCurrentBoundaryPathIndex( -1 ),
+ bPatternLineOffsetSet( sal_False ),
nFlags( 0 ),
nAssociativityFlag( 0 ),
nBoundaryPathCount( 0 ),
@@ -695,6 +709,10 @@ DXFHatchEntity::DXFHatchEntity() :
nHatchPatternDefinitionLines( 0 ),
fPixelSize( 1.0 ),
nNumberOfSeedPoints( 0 ),
+ bHasPatternLine( sal_False ),
+ fPatternLineAngle( 0.0 ),
+ fPatternLineOffsetX( 0.0 ),
+ fPatternLineOffsetY( 0.0 ),
pBoundaryPathData( NULL )
{
}
@@ -741,6 +759,21 @@ void DXFHatchEntity::EvaluateGroup( DXFGroupReader & rDGR )
case 47 : fPixelSize = rDGR.GetF(); break;
case 98 : nNumberOfSeedPoints = rDGR.GetI(); break;
+ // Pattern-definition lines (only present for a pattern fill,
after 75).
+ // Capture the FIRST line's angle (53) and inter-line offset
(45/46);
+ // 53/45/46 appear nowhere else in a HATCH, so intercepting
them here is
+ // safe. Codes 43/44 (base point) are not needed for a VCL
Hatch.
+ case 53 :
+ if ( !bHasPatternLine ) { fPatternLineAngle =
rDGR.GetF(); bHasPatternLine = sal_True; }
+ break;
+ case 45 :
+ if ( bHasPatternLine && !bPatternLineOffsetSet )
fPatternLineOffsetX = rDGR.GetF();
+ break;
+ case 46 :
+ if ( bHasPatternLine && !bPatternLineOffsetSet )
+ { fPatternLineOffsetY = rDGR.GetF();
bPatternLineOffsetSet = sal_True; }
+ break;
+
//!! passthrough !!
case 92 : nCurrentBoundaryPathIndex++;
default:
@@ -842,6 +875,98 @@ void DXFDimensionEntity::EvaluateGroup(DXFGroupReader &
rDGR)
//---------------------------- DXFEntites
--------------------------------------
+//--------------------------DXFEllipseEntity------------------------------------
+
+DXFEllipseEntity::DXFEllipseEntity() : DXFBasicEntity(DXF_ELLIPSE)
+{
+ fRatio = 1.0;
+ fStart = 0.0;
+ fEnd = 2*3.14159265359;
+}
+
+void DXFEllipseEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 11: aP1.fx=rDGR.GetF(); break;
+ case 21: aP1.fy=rDGR.GetF(); break;
+ case 31: aP1.fz=rDGR.GetF(); break;
+ case 40: fRatio=rDGR.GetF(); break;
+ case 41: fStart=rDGR.GetF(); break;
+ case 42: fEnd=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFSplineEntity-------------------------------------
+
+DXFSplineEntity::DXFSplineEntity() :
+ DXFBasicEntity(DXF_SPLINE),
+ nFlags(0),
+ nDegree(0),
+ nKnotCount(0),
+ nCtrlCount(0),
+ nFitCount(0),
+ pfKnots(NULL),
+ pControlPts(NULL),
+ nKnotIndex(0),
+ nCtrlIndex(0)
+{
+}
+
+DXFSplineEntity::~DXFSplineEntity()
+{
+ delete[] pfKnots;
+ delete[] pControlPts;
+}
+
+void DXFSplineEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 70: nFlags = rDGR.GetI(); break;
+ case 71: nDegree = rDGR.GetI(); break;
+ case 72:
+ nKnotCount = rDGR.GetI();
+ if ( rDGR.GetStatus() && nKnotCount > 0 ) {
+ try { pfKnots = new double[nKnotCount]; }
+ catch (::std::bad_alloc) { rDGR.SetError(); }
+ }
+ else if ( nKnotCount < 0 )
+ rDGR.SetError();
+ break;
+ case 73:
+ nCtrlCount = rDGR.GetI();
+ if ( rDGR.GetStatus() && nCtrlCount > 0 ) {
+ try { pControlPts = new DXFVector[nCtrlCount]; }
+ catch (::std::bad_alloc) { rDGR.SetError(); }
+ }
+ else if ( nCtrlCount < 0 )
+ rDGR.SetError();
+ break;
+ case 74: nFitCount = rDGR.GetI(); break;
+ case 40:
+ if ( pfKnots && nKnotIndex < nKnotCount )
+ pfKnots[nKnotIndex++] = rDGR.GetF();
+ break;
+ // control points are 3D (10/20/30); advance on the Z group
+ case 10:
+ if ( pControlPts && nCtrlIndex < nCtrlCount )
+ pControlPts[nCtrlIndex].fx = rDGR.GetF();
+ break;
+ case 20:
+ if ( pControlPts && nCtrlIndex < nCtrlCount )
+ pControlPts[nCtrlIndex].fy = rDGR.GetF();
+ break;
+ case 30:
+ if ( pControlPts && nCtrlIndex < nCtrlCount )
+ pControlPts[nCtrlIndex++].fz = rDGR.GetF();
+ break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
void DXFEntities::Read(DXFGroupReader & rDGR)
{
DXFBasicEntity * pE, * * ppSucc;
@@ -874,6 +999,8 @@ void DXFEntities::Read(DXFGroupReader & rDGR)
else if (strcmp(rDGR.GetS(),"3DFACE" )==0) pE=new
DXF3DFaceEntity;
else if (strcmp(rDGR.GetS(),"DIMENSION" )==0) pE=new
DXFDimensionEntity;
else if (strcmp(rDGR.GetS(),"HATCH" )==0) pE=new
DXFHatchEntity;
+ else if (strcmp(rDGR.GetS(),"ELLIPSE" )==0) pE=new
DXFEllipseEntity;
+ else if (strcmp(rDGR.GetS(),"SPLINE" )==0) pE=new
DXFSplineEntity;
else
{
do {
@@ -897,3 +1024,25 @@ void DXFEntities::Clear()
delete ptmp;
}
}
+
+sal_Bool DXFCoordsAreWCS(const DXFBasicEntity & rE)
+{
+ // See the header comment. For a LINE the extrusion only defines a
thickness
+ // direction, never the endpoint positions; applying it scatters 3D
wireframes
+ // (issue 99893) and inflates the bounding box (both must agree).
ELLIPSE/SPLINE
+ // are intentionally NOT WCS here — DrawEllipseEntity/DrawSplineEntity
compute in
+ // the entity's own frame and rely on the extrusion being applied.
+ switch (rE.eType) {
+ case DXF_LINE:
+ case DXF_POINT:
+ case DXF_3DFACE:
+ return sal_True;
+ case DXF_POLYLINE:
+ // 3D polyline / 3D mesh / polyface mesh store WCS
vertices; a plain
+ // 2D polyline is in OCS.
+ return ( ((const DXFPolyLineEntity &)rE).nFlags &
(8|16|64) ) != 0
+ ? sal_True : sal_False;
+ default:
+ return sal_False;
+ }
+}
diff --git a/main/filter/source/graphicfilter/idxf/dxfentrd.hxx
b/main/filter/source/graphicfilter/idxf/dxfentrd.hxx
index 035082d72a..3c08f08a39 100644
--- a/main/filter/source/graphicfilter/idxf/dxfentrd.hxx
+++ b/main/filter/source/graphicfilter/idxf/dxfentrd.hxx
@@ -53,7 +53,9 @@ enum DXFEntityType {
DXF_3DFACE,
DXF_DIMENSION,
DXF_LWPOLYLINE,
- DXF_HATCH
+ DXF_HATCH,
+ DXF_ELLIPSE,
+ DXF_SPLINE
};
//------------------------------------------------------------------------------
@@ -369,6 +371,7 @@ class DXFLWPolyLineEntity : public DXFBasicEntity
double fEndWidth; // 41
DXFVector* pP;
+ double* pBulge; // 42 (per vertex;
parallel to pP, 0.0 = straight segment)
DXFLWPolyLineEntity();
~DXFLWPolyLineEntity();
@@ -465,6 +468,7 @@ class DXFHatchEntity : public DXFBasicEntity
{
sal_Bool bIsInBoundaryPathContext;
sal_Int32 nCurrentBoundaryPathIndex;
+ sal_Bool bPatternLineOffsetSet; //
transient parse state (first 45/46 captured)
public :
@@ -481,6 +485,15 @@ class DXFHatchEntity : public DXFBasicEntity
double fPixelSize;
// 47
sal_Int32 nNumberOfSeedPoints; // 98
+ // First pattern-definition line (78 block). Enough to drive a
VCL Hatch
+ // (single/double/triple): the line angle (53) and the
inter-line offset
+ // (45/46) give on-page angle + spacing; multi-line patterns
collapse to
+ // this line's geometry plus the style from
nHatchPatternDefinitionLines.
+ sal_Bool bHasPatternLine;
// a 53 line angle was parsed
+ double fPatternLineAngle;
// 53
+ double fPatternLineOffsetX; // 45
+ double fPatternLineOffsetY; // 46
+
DXFBoundaryPathData* pBoundaryPathData;
DXFHatchEntity();
@@ -492,6 +505,53 @@ class DXFHatchEntity : public DXFBasicEntity
};
+//--------------------------Ellipse---------------------------------------------
+
+class DXFEllipseEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30 center
+ DXFVector aP1; // 11,21,31 endpoint of the major axis, relative to
center
+ double fRatio; // 40 ratio of minor axis to major axis
+ double fStart; // 41 start parameter (radians; 0 for a full
ellipse)
+ double fEnd; // 42 end parameter (radians; 2*pi for a full
ellipse)
+
+ DXFEllipseEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR);
+};
+
+//--------------------------Spline----------------------------------------------
+
+class DXFSplineEntity : public DXFBasicEntity {
+
+public:
+
+ long nFlags; // 70 bit 1=closed, 2=periodic, 4=rational,
8=planar
+ long nDegree; // 71
+ long nKnotCount; // 72
+ long nCtrlCount; // 73
+ long nFitCount; // 74
+
+ double * pfKnots; // 40 (nKnotCount entries)
+ DXFVector * pControlPts; // 10,20,30 (nCtrlCount entries)
+
+ DXFSplineEntity();
+ ~DXFSplineEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR);
+
+private:
+
+ long nKnotIndex;
+ long nCtrlIndex;
+};
+
//--------------------------Vertex----------------------------------------------
class DXFVertexEntity : public DXFBasicEntity {
@@ -577,6 +637,14 @@ public:
// Loescht alle Entities
};
+// True when an entity's stored coordinates are already in WCS, so the OCS
+// "arbitrary axis" (extrusion) transform must NOT be applied to them. Used by
+// BOTH the renderer (DrawEntities) and the bounding-box pass
(CalcBoundingBox) so
+// they stay consistent: flat/planar entities (CIRCLE, ARC, TEXT, 2D POLYLINE,
+// LWPOLYLINE, INSERT, ...) are OCS and DO need the extrusion; inherently-3D
+// entities (LINE, POINT, 3DFACE, and a 3D polyline / mesh) carry WCS
coordinates.
+sal_Bool DXFCoordsAreWCS(const DXFBasicEntity & rE);
+
//------------------------------------------------------------------------------
//--------------------------------- inlines
------------------------------------
//------------------------------------------------------------------------------
diff --git a/main/filter/source/graphicfilter/idxf/dxfgrprd.cxx
b/main/filter/source/graphicfilter/idxf/dxfgrprd.cxx
index 57d362a33c..895469ece9 100644
--- a/main/filter/source/graphicfilter/idxf/dxfgrprd.cxx
+++ b/main/filter/source/graphicfilter/idxf/dxfgrprd.cxx
@@ -151,7 +151,7 @@ sal_uInt16 DXFGroupReader::Read()
else if (nG< 140) ReadS( aTmp );
else if (nG< 148) F140_147[nG-140]=ReadF();
else if (nG< 170) ReadS( aTmp );
- else if (nG< 176) I170_175[nG-175]=ReadI();
+ else if (nG< 176) I170_175[nG-170]=ReadI();
else if (nG< 180) ReadI();
else if (nG< 210) ReadS( aTmp );
else if (nG< 240) F210_239[nG-210]=ReadF();
@@ -330,6 +330,18 @@ long DXFGroupReader::ReadI()
}
while (*p==0x20) p++;
+
+ // Tolerate an integer group value written in floating-point notation
+ // (e.g. "1.0"): some CAD applications (Pro/ENGINEER) emit integer group
+ // codes — such as the DIMSTYLE flags 71/72 — that way. Skip a trailing
+ // decimal fraction and keep the integer part; AutoCAD accepts this,
whereas
+ // a strict reject aborts the whole import (issue 122565).
+ if (*p=='.') {
+ p++;
+ while (*p>='0' && *p<='9') p++;
+ while (*p==0x20) p++;
+ }
+
if (*p!=0) {
bStatus=sal_False;
return 0;
diff --git a/main/filter/source/graphicfilter/idxf/dxfreprd.cxx
b/main/filter/source/graphicfilter/idxf/dxfreprd.cxx
index 205537a9a1..6cc60e1aa6 100644
--- a/main/filter/source/graphicfilter/idxf/dxfreprd.cxx
+++ b/main/filter/source/graphicfilter/idxf/dxfreprd.cxx
@@ -25,6 +25,10 @@
#include "precompiled_filter.hxx"
#include <string.h>
+#include <math.h>
+#include <tools/string.hxx> // gsl_getSystemTextEncoding
+#include <rtl/tencinfo.h> // rtl_getTextEncodingFromWindowsCodePage
+#include <rtl/string.h> // rtl_str_compareIgnoreAsciiCase
#include <dxfreprd.hxx>
@@ -135,7 +139,32 @@ void DXFPalette::SetColor(sal_uInt8 nIndex, sal_uInt8
nRed, sal_uInt8 nGreen, sa
DXFRepresentation::DXFRepresentation()
{
- setTextEncoding(RTL_TEXTENCODING_IBM_437);
+ // Default text encoding for a DXF that neither declares a $DWGCODEPAGE
nor
+ // receives an override from the caller (see GraphicImport /
FilterConfigItem).
+ // The historic hard-coded IBM_437 (DOS OEM) is almost never right for
a modern
+ // DXF and garbles any high-bit text (issue 99892). Prefer the system
encoding,
+ // which usually matches the locale the file came from (e.g. a Russian
machine
+ // -> MS_1251).
+ //
+ // BUT the system encoding is not necessarily a legacy 8-bit code page
any more:
+ // on Windows with the "Use Unicode UTF-8 for worldwide language
support" option
+ // GetACP() returns 65001, and on modern Linux the locale is UTF-8 as
well, so
+ // the system encoding is RTL_TEXTENCODING_UTF8. Legacy DXF text
predates UTF-8
+ // and is never encoded that way (test13.dxf: windows-1252 Ä/ü/ß on a
UTF-8 host
+ // came out as artefacts), so decoding those bytes as UTF-8 fails. When
the
+ // system encoding is Unicode/unknown, fall back to windows-1252 — the
most
+ // common encoding for untagged DXF and AOO's usual Western default
(cf. the
+ // msfilter legacy importers); other scripts come from an explicit
$DWGCODEPAGE
+ // or the Phase B chooser.
+ // Precedence: file $DWGCODEPAGE > caller override > this default.
+ rtl_TextEncoding eDefault = gsl_getSystemTextEncoding();
+ if ( eDefault == RTL_TEXTENCODING_UTF8 ||
+ eDefault == RTL_TEXTENCODING_UTF7 ||
+ eDefault == RTL_TEXTENCODING_UCS2 ||
+ eDefault == RTL_TEXTENCODING_UCS4 ||
+ eDefault == RTL_TEXTENCODING_DONTKNOW )
+ eDefault = RTL_TEXTENCODING_MS_1252;
+ setTextEncoding(eDefault);
setGlobalLineTypeScale(1.0);
}
@@ -176,13 +205,67 @@ sal_Bool DXFRepresentation::Read( SvStream & rIStream,
sal_uInt16 nMinPercent, s
delete pDGR;
- if (bRes==sal_True && aBoundingBox.bEmpty==sal_True)
- CalcBoundingBox(aEntities,aBoundingBox);
+ if (bRes==sal_True)
+ {
+ // The header $EXTMIN/$EXTMAX that ReadHeader collected into
aBoundingBox
+ // are written by AutoCAD from the current view/regen state,
not from a
+ // tight geometry fit, so they drift with the zoom level in
effect when
+ // the file was saved (issue 58347: same drawing, different
zoom, wrong
+ // import scale). Measure the real geometry extent and use that
for the
+ // page-fit scaling instead. CalcBoundingBox now measures every
entity
+ // type DrawEntities actually renders (HATCH included), so a
non-empty
+ // geometry box is authoritative. The header extents are kept
only as a
+ // last-resort fallback for the degenerate case where nothing
measurable
+ // was drawn at all (empty geometry box).
+ DXFBoundingBox aGeometryBox;
+ CalcBoundingBox(aEntities,aGeometryBox);
+ if (aGeometryBox.bEmpty==sal_False)
+ aBoundingBox=aGeometryBox;
+ }
return bRes;
}
+// Map a DXF $DWGCODEPAGE value to an rtl text encoding.
+// The values AutoCAD writes are "ANSI_<cp>" (e.g. ANSI_1252, ANSI_1251),
+// "DOS<cp>" (e.g. DOS932, DOS850) or a few named ones ("MACINTOSH", "UTF8").
+// For the ANSI_/DOS_ forms the trailing number IS the Windows code page, so we
+// extract it and let rtl resolve it (covers the whole family in one line
instead
+// of the old single ANSI_932 special case). Returns RTL_TEXTENCODING_DONTKNOW
+// when the value is empty/unrecognised so the caller can keep its current
+// (default or caller-supplied) encoding.
+static rtl_TextEncoding DXFCodePageToTextEncoding(const char * pCodePage)
+{
+ if (pCodePage==NULL || *pCodePage==0)
+ return RTL_TEXTENCODING_DONTKNOW;
+
+ // find the first digit (start of the code-page number, if any)
+ const char * p = pCodePage;
+ while (*p!=0 && (*p<'0' || *p>'9'))
+ p++;
+ if (*p!=0) {
+ sal_uInt32 nCodePage = 0;
+ while (*p>='0' && *p<='9') {
+ nCodePage = nCodePage*10 + (sal_uInt32)(*p-'0');
+ p++;
+ }
+ rtl_TextEncoding eEnc =
rtl_getTextEncodingFromWindowsCodePage(nCodePage);
+ if (eEnc!=RTL_TEXTENCODING_DONTKNOW)
+ return eEnc;
+ }
+
+ // named values without a code-page number
+ if (rtl_str_compareIgnoreAsciiCase(pCodePage,"MACINTOSH")==0)
+ return RTL_TEXTENCODING_APPLE_ROMAN;
+ if (rtl_str_compareIgnoreAsciiCase(pCodePage,"UTF8")==0 ||
+ rtl_str_compareIgnoreAsciiCase(pCodePage,"UTF-8")==0)
+ return RTL_TEXTENCODING_UTF8;
+
+ return RTL_TEXTENCODING_DONTKNOW;
+}
+
+
void DXFRepresentation::ReadHeader(DXFGroupReader & rDGR)
{
@@ -208,15 +291,13 @@ void DXFRepresentation::ReadHeader(DXFGroupReader & rDGR)
{
rDGR.Read();
- // FIXME: we really need a whole
table of
- // $DWGCODEPAGE to encodings mappings
- if (
(strcmp(rDGR.GetS(),"ANSI_932")==0) ||
-
(strcmp(rDGR.GetS(),"ansi_932")==0) ||
-
(strcmp(rDGR.GetS(),"DOS932")==0) ||
-
(strcmp(rDGR.GetS(),"dos932")==0) )
- {
-
setTextEncoding(RTL_TEXTENCODING_MS_932);
- }
+ // The file declares its encoding:
this is
+ // authoritative and overrides both
the
+ // default and any caller-supplied
override.
+ rtl_TextEncoding eEnc =
+
DXFCodePageToTextEncoding(rDGR.GetS());
+ if (eEnc!=RTL_TEXTENCODING_DONTKNOW)
+ setTextEncoding(eEnc);
}
else if (strcmp(rDGR.GetS(),"$LTSCALE")==0)
{
@@ -231,73 +312,150 @@ 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,
+// growing up and to the right (the justification codes are ignored by the
+// 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.
+// 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) {
+ UnionOCS(rBox, rE2W, rInsert);
+ return;
+ }
+ if (fXScale <= 0.0)
+ fXScale = 1.0;
+
+ const size_t nLen = (pText != NULL) ? strlen(pText) : 0;
+ const double fWidth = (double)nLen * 0.85 * fHeight * fXScale;
+ const double fAscent = fHeight;
+ const double fDescent = 0.25 * fHeight;
+
+ const double fRad = fRotAngle * 3.14159265359 / 180.0;
+ const DXFVector aDir( cos(fRad), sin(fRad), 0.0); // text direction
+ const DXFVector aUp (-sin(fRad), cos(fRad), 0.0); // baseline normal
(up)
+
+ for (int a = 0; a < 2; a++) {
+ const double fAlong = a ? fWidth : 0.0;
+ for (int b = 0; b < 2; b++) {
+ const double fPerp = b ? fAscent : -fDescent;
+ UnionOCS(rBox, rE2W, rInsert + aDir * fAlong + aUp *
fPerp);
+ }
+ }
+}
+
+
void DXFRepresentation::CalcBoundingBox(const DXFEntities & rEntities,
DXFBoundingBox & rBox)
{
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. Use the same
+ // WCS-vs-OCS rule as DrawEntities (DXFCoordsAreWCS): entities
whose coords
+ // are already WCS (LINE/POINT/3DFACE/3D-polyline) must NOT be
extruded, or
+ // the box is measured over scattered positions and comes out
far too large
+ // (issue 99893/70273 — geometry then sits in a corner of an
inflated page).
+ DXFTransform aE2W; // identity by default
+ if (pBE->aExtrusion.fz != 1.0 && !DXFCoordsAreWCS(*pBE))
+ 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);
+ // Measure the arc's SWEPT extent, not its full
circle. A
+ // large-radius arc (nearly straight, common in
CAD) has its
+ // centre far outside the drawing; unioning
centre +/- radius
+ // blew the box up enormously and collapsed the
real geometry
+ // (issue 122565). Sample along the sweep
exactly as the renderer
+ // does (CCW from fStart by fdA, normalised to
(0,360]) and map
+ // each point through the same ECS->WCS
transform.
+ double fA1 = pE->fStart;
+ double fdA = pE->fEnd - fA1;
+ while (fdA>=360.0) fdA-=360.0;
+ while (fdA<=0.0) fdA+=360.0;
+ sal_uInt16 nSeg = (sal_uInt16)(fdA/4.0 + 0.5);
+ if (nSeg<1) nSeg=1;
+ for (sal_uInt16 nA=0; nA<=nSeg; nA++) {
+ double fAng = 3.14159265359/180.0 *
+ (fA1 +
fdA*(double)nA/(double)nSeg);
+ UnionOCS(rBox, aE2W, pE->aP0 +
+
DXFVector(pE->fRadius*cos(fAng),
+
pE->fRadius*sin(fAng), 0.0));
+ }
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;
- //???
+ const DXFTextEntity * pE = (DXFTextEntity*)pBE;
+ UnionTextExtent(rBox, aE2W, pE->aP0,
pE->fHeight, pE->fXScale,
+ pE->fRotAngle,
pE->sText);
break;
}
case DXF_SHAPE: {
- //const DXFShapeEntity * pE =
(DXFShapeEntity*)pBE;
- //???
+ // SHAPE is not rendered (DrawEntities'
dispatch has no case for
+ // it, so DrawEntities falls through to
default:break), therefore
+ // it deliberately does not contribute to the
extent — measuring
+ // invisible geometry would only reserve empty
margin and shrink
+ // the visible drawing on the page.
break;
}
case DXF_INSERT: {
@@ -312,34 +470,37 @@ 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: {
- //const DXFAttDefEntity * pE =
(DXFAttDefEntity*)pBE;
- //???
+ // ATTDEF (attribute definition) is likewise
not rendered by
+ // DrawEntities, so — like SHAPE — it is
intentionally left out
+ // of the extent to stay consistent with what
actually gets
+ // drawn.
break;
}
case DXF_ATTRIB: {
- //const DXFAttribEntity * pE =
(DXFAttribEntity*)pBE;
- //???
+ const DXFAttribEntity * pE =
(DXFAttribEntity*)pBE;
+ 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: {
@@ -354,16 +515,18 @@ 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: {
- //const DXFAttribEntity * pE =
(DXFAttribEntity*)pBE;
- //???
+ // The old-style POLYLINE header carries no
coordinates itself;
+ // its geometry lives in the VERTEX entities
that follow it in
+ // the entity list (each handled by the
DXF_VERTEX case above),
+ // so there is nothing to union here.
break;
}
case DXF_SEQEND: {
@@ -371,10 +534,77 @@ void DXFRepresentation::CalcBoundingBox(const DXFEntities
& rEntities,
//???
break;
}
- case DXF_HATCH :
+ case DXF_HATCH : {
+ // HATCH is rendered (DrawHatchEntity), so its
extent has to be
+ // measured or a hatch reaching past the rest
of the drawing gets
+ // cropped. Union exactly the boundary geometry
the renderer
+ // actually draws: the points of a polyline
boundary path, and
+ // the endpoints of straight (type 1) edges.
Circular-arc,
+ // elliptical-arc and spline edges are NOT
drawn by
+ // DrawHatchEntity (their code is commented out
/ empty), so they
+ // are intentionally not unioned here —
matching the render.
+ const DXFHatchEntity * pE =
(DXFHatchEntity*)pBE;
+ for (sal_Int32 nPath = 0;
+ pE->pBoundaryPathData != NULL && nPath
< pE->nBoundaryPathCount;
+ nPath++)
+ {
+ const DXFBoundaryPathData & rPath =
pE->pBoundaryPathData[nPath];
+ if (rPath.bIsPolyLine == sal_True) {
+ if (rPath.pP != NULL) {
+ for (sal_Int32 i = 0; i
< rPath.nPointCount; i++)
+ UnionOCS(rBox,
aE2W, rPath.pP[i]);
+ }
+ }
+ else {
+ for (size_t i = 0; i <
rPath.aEdges.size(); i++) {
+ const DXFEdgeType *
pEdge = rPath.aEdges[i];
+ if (pEdge != NULL &&
pEdge->nEdgeType == 1) {
+ const
DXFEdgeTypeLine * pLine = (DXFEdgeTypeLine*)pEdge;
+ UnionOCS(rBox,
aE2W, pLine->aStartPoint);
+ UnionOCS(rBox,
aE2W, pLine->aEndPoint);
+ }
+ }
+ }
+ }
break;
- case DXF_LWPOLYLINE :
+ }
+ case DXF_LWPOLYLINE : {
+ // LWPOLYLINE keeps its vertices inline in pP[]
(not as
+ // separate VERTEX entities), so they must be
unioned here
+ // or the drawing extent misses the polyline
and the graphic
+ // is cropped. Walk the same [0,nCount) range
that
+ // DrawLWPolyLineEntity renders.
+ const DXFLWPolyLineEntity * pE =
(DXFLWPolyLineEntity*)pBE;
+ if (pE->pP != NULL) {
+ for (sal_Int32 i = 0; i < pE->nCount;
i++)
+ UnionOCS(rBox, aE2W, pE->pP[i]);
+ }
break;
+ }
+ case DXF_ELLIPSE: {
+ // Conservative full-ellipse AABB (like ARC
uses the full-circle
+ // box): centre +/- the axis-projected radii.
Over-approximates a
+ // partial ellipse, which is fine for a
bounding box.
+ const DXFEllipseEntity * pE =
(DXFEllipseEntity*)pBE;
+ const DXFVector & aU = pE->aP1;
+ DXFVector aV(-aU.fy*pE->fRatio,
aU.fx*pE->fRatio, 0.0);
+ double fRx = sqrt(aU.fx*aU.fx + aV.fx*aV.fx);
+ double fRy = sqrt(aU.fy*aU.fy + aV.fy*aV.fy);
+ DXFVector aC;
+ aE2W.Transform(pE->aP0, aC);
+ DXFVector aP=aC; aP.fx-=fRx; aP.fy-=fRy;
rBox.Union(aP);
+ aP=aC; aP.fx+=fRx; aP.fy+=fRy; rBox.Union(aP);
+ break;
+ }
+ case DXF_SPLINE: {
+ // The control polygon's convex hull contains
the B-spline curve,
+ // so unioning the control points bounds it
(conservatively).
+ const DXFSplineEntity * pE =
(DXFSplineEntity*)pBE;
+ if (pE->pControlPts != NULL)
+ for (long i = 0; i < pE->nCtrlCount;
i++)
+ UnionOCS(rBox, aE2W,
pE->pControlPts[i]);
+ break;
+ }
}
pBE=pBE->pSucc;
}
diff --git a/main/filter/source/graphicfilter/idxf/dxftblrd.cxx
b/main/filter/source/graphicfilter/idxf/dxftblrd.cxx
index ceab0b7c37..afbcd2107e 100644
--- a/main/filter/source/graphicfilter/idxf/dxftblrd.cxx
+++ b/main/filter/source/graphicfilter/idxf/dxftblrd.cxx
@@ -332,11 +332,29 @@ DXFLayer * DXFTables::SearchLayer(const char * pName)
const
}
+// DXF symbol-table names (e.g. the "*ACTIVE" viewport) are case-insensitive,
but
+// applications write them in different case ("*ACTIVE" vs "*Active"). A plain
+// strcmp then misses the active viewport, so idxf discards the saved view and
+// flattens a 3D drawing to a top-view. Compare ASCII-case-insensitively (names
+// are ASCII; this avoids <ctype.h>/locale and cherry-picks cleanly).
+static sal_Bool DXFNameEqualIgnoreCase(const char * p1, const char * p2)
+{
+ while (*p1!=0 && *p2!=0) {
+ char c1=*p1, c2=*p2;
+ if (c1>='A' && c1<='Z') c1 = (char)(c1 - 'A' + 'a');
+ if (c2>='A' && c2<='Z') c2 = (char)(c2 - 'A' + 'a');
+ if (c1!=c2) return sal_False;
+ p1++; p2++;
+ }
+ return (*p1==0 && *p2==0) ? sal_True : sal_False;
+}
+
+
DXFVPort * DXFTables::SearchVPort(const char * pName) const
{
DXFVPort * p;
for (p=pVPorts; p!=NULL; p=p->pSucc) {
- if (strcmp(pName,p->sName)==0) break;
+ if (DXFNameEqualIgnoreCase(pName,p->sName)) break;
}
return p;
}
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;
}
diff --git a/main/filter/source/graphicfilter/idxf/idxf.cxx
b/main/filter/source/graphicfilter/idxf/idxf.cxx
index a1fd94a11c..f44cf85278 100644
--- a/main/filter/source/graphicfilter/idxf/idxf.cxx
+++ b/main/filter/source/graphicfilter/idxf/idxf.cxx
@@ -28,17 +28,34 @@
#include <vcl/gdimtf.hxx>
#include <vcl/graph.hxx>
#include <vcl/virdev.hxx>
+#include <svtools/FilterConfigItem.hxx>
#include "dxf2mtf.hxx"
#include <math.h>
//================== GraphicImport - die exportierte Funktion ================
-extern "C" sal_Bool __LOADONCALLAPI GraphicImport(SvStream & rStream, Graphic
& rGraphic, FilterConfigItem*, sal_Bool )
+extern "C" sal_Bool __LOADONCALLAPI GraphicImport(SvStream & rStream, Graphic
& rGraphic, FilterConfigItem* pConfigItem, sal_Bool )
{
DXFRepresentation aDXF;
DXF2GDIMetaFile aConverter;
GDIMetaFile aMTF;
+ // A DXF may not declare its text encoding ($DWGCODEPAGE). In that case
the
+ // caller (a higher layer that knows the user's intent) can supply a
fallback
+ // encoding as a "CharacterSet" filter option; it is applied before
reading so
+ // that a $DWGCODEPAGE actually present in the file still wins over it.
+ // See idxf/cases/case-99892 (Phase B concept) for how this option is
meant to
+ // be populated. When neither the file nor the caller provides one,
+ // DXFRepresentation defaults to the system ANSI encoding.
+ if ( pConfigItem )
+ {
+ sal_Int32 nEncoding = pConfigItem->ReadInt32(
+ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
"CharacterSet" ) ),
+ RTL_TEXTENCODING_DONTKNOW );
+ if ( nEncoding != RTL_TEXTENCODING_DONTKNOW )
+ aDXF.setTextEncoding( (rtl_TextEncoding)nEncoding );
+ }
+
if ( aDXF.Read( rStream, 0, 60 ) == sal_False )
return sal_False;
if ( aConverter.Convert( aDXF, aMTF, 60, 100 ) == sal_False )
diff --git a/main/filter/source/graphicfilter/idxf/makefile.mk
b/main/filter/source/graphicfilter/idxf/makefile.mk
index ba69c76043..71a2d76637 100644
--- a/main/filter/source/graphicfilter/idxf/makefile.mk
+++ b/main/filter/source/graphicfilter/idxf/makefile.mk
@@ -54,7 +54,7 @@ SLOFILES = $(SLO)$/dxfgrprd.obj \
SHL1TARGET= idx$(DLLPOSTFIX)
SHL1IMPLIB= idxf
-SHL1STDLIBS= $(VCLLIB) $(TOOLSLIB) $(SALLIB)
+SHL1STDLIBS= $(VCLLIB) $(TOOLSLIB) $(SALLIB) $(SVTOOLLIB)
.IF "$(GUI)" == "OS2"
SHL1STDLIBS+= $(CPPULIB)
.ENDIF
diff --git a/main/svtools/source/filter/filter.cxx
b/main/svtools/source/filter/filter.cxx
index 6558b7cf38..bf1ed23db0 100644
--- a/main/svtools/source/filter/filter.cxx
+++ b/main/svtools/source/filter/filter.cxx
@@ -514,8 +514,32 @@ static sal_Bool ImpPeekGraphicFormat( SvStream& rStream,
String& rFormatExtensio
bSomethingTested=sal_True;
i=0;
- while (i<256 && sFirstBytes[i]<=32)
- i++;
+
+ // A DXF file may begin with one or more 999 comment groups
(each is a
+ // "999" code line followed by a comment value line), e.g.
files written
+ // by Pro/ENGINEER. Skip them before looking for the first real
group,
+ // otherwise such valid DXF files are rejected as an unknown
format
+ // (issue 122565).
+ for (;;)
+ {
+ while (i<256 && sFirstBytes[i]<=32)
+ i++;
+ if ( i+3<256 && sFirstBytes[i]=='9' &&
sFirstBytes[i+1]=='9' &&
+ sFirstBytes[i+2]=='9' && sFirstBytes[i+3]<=32 )
+ {
+ // skip the "999" code line and the comment
value line after it
+ int nLine;
+ for ( nLine=0; nLine<2 && i<256; nLine++ )
+ {
+ while (i<256 && sFirstBytes[i]!='\n' &&
sFirstBytes[i]!='\r')
+ i++;
+ while (i<256 && (sFirstBytes[i]=='\n'
|| sFirstBytes[i]=='\r'))
+ i++;
+ }
+ continue;
+ }
+ break;
+ }
if (i<256)
{