xref: /trunk/main/filter/source/graphicfilter/idxf/dxf2mtf.cxx (revision c082241a9b512a7cac07a6b4facb9ec65953be75)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_filter.hxx"
26 
27 #include <string.h>
28 #include <vcl/gdimtf.hxx>
29 #include <vcl/virdev.hxx>
30 #include <vcl/hatch.hxx>
31 #include <tools/poly.hxx>
32 #include "dxf2mtf.hxx"
33 
34 #include <math.h>
35 
36 
CountEntities(const DXFEntities & rEntities)37 sal_uLong DXF2GDIMetaFile::CountEntities(const DXFEntities & rEntities)
38 {
39     const DXFBasicEntity * pBE;
40     sal_uLong nRes;
41 
42     nRes=0;
43     for (pBE=rEntities.pFirst; pBE!=NULL; pBE=pBE->pSucc) nRes++;
44     return nRes;
45 }
46 
47 
MayCallback(sal_uLong)48 void DXF2GDIMetaFile::MayCallback(sal_uLong /*nMainEntitiesProcessed*/)
49 {
50     // sal_uLong nPercent;
51 /*
52     if (pCallback!=NULL && nMainEntitiesCount!=0) {
53         nPercent=nMinPercent+(nMaxPercent-nMinPercent)*nMainEntitiesProcessed/nMainEntitiesCount;
54         if (nPercent>=nLastPercent+4) {
55             if (((*pCallback)(pCallerData,(sal_uInt16)nPercent))==sal_True) bStatus=sal_False;
56             nLastPercent=nPercent;
57         }
58     }
59 */
60 }
61 
ConvertColor(sal_uInt8 nColor)62 Color DXF2GDIMetaFile::ConvertColor(sal_uInt8 nColor)
63 {
64     return Color(
65         pDXF->aPalette.GetRed( nColor ),
66         pDXF->aPalette.GetGreen( nColor ),
67         pDXF->aPalette.GetBlue( nColor ) );
68 }
69 
GetEntityColor(const DXFBasicEntity & rE)70 long DXF2GDIMetaFile::GetEntityColor(const DXFBasicEntity & rE)
71 {
72     long nColor;
73     const DXFLayer * pLayer;
74 
75     nColor=rE.nColor;
76     if (nColor==256) {
77         if (rE.sLayer[0]=='0' && rE.sLayer[1]==0) nColor=nParentLayerColor;
78         else {
79             pLayer=pDXF->aTables.SearchLayer(rE.sLayer);
80             if (pLayer!=NULL) nColor=pLayer->nColor;
81             else nColor=nParentLayerColor;
82         }
83     }
84     else if (nColor==0) nColor=nBlockColor;
85     return nColor;
86 }
87 
LTypeToDXFLineInfo(const char * sLineType)88 DXFLineInfo DXF2GDIMetaFile::LTypeToDXFLineInfo(const char * sLineType)
89 {
90     const DXFLType * pLT;
91     DXFLineInfo aDXFLineInfo;
92 
93     pLT=pDXF->aTables.SearchLType(sLineType);
94     if (pLT==NULL || pLT->nDashCount == 0) {
95         aDXFLineInfo.eStyle = LINE_SOLID;
96     }
97     else {
98         sal_Int32 i;
99         double x;
100         aDXFLineInfo.eStyle = LINE_DASH;
101         for (i=0; i < (pLT->nDashCount); i++) {
102             x = pLT->fDash[i] * pDXF->getGlobalLineTypeScale();
103 // ####
104             // x = (sal_Int32) rTransform.TransLineWidth( pLT->fDash[i] * pDXF->getGlobalLineTypeScale() );
105             if ( x >= 0.0 ) {
106                 if ( aDXFLineInfo.nDotCount == 0 ) {
107                     aDXFLineInfo.nDotCount ++;
108                     aDXFLineInfo.fDotLen = x;
109                 }
110                 else if ( aDXFLineInfo.fDotLen == x ) {
111                     aDXFLineInfo.nDotCount ++;
112                 }
113                 else if ( aDXFLineInfo.nDashCount == 0 ) {
114                     aDXFLineInfo.nDashCount ++;
115                     aDXFLineInfo.fDashLen = x;
116                 }
117                 else if ( aDXFLineInfo.fDashLen == x ) {
118                     aDXFLineInfo.nDashCount ++;
119                 }
120                 else {
121                     // It is impossible to be converted.
122                 }
123             }
124             else {
125                 if ( aDXFLineInfo.fDistance == 0 ) {
126                     aDXFLineInfo.fDistance = -1 * x;
127                 }
128                 else {
129                     // It is impossible to be converted.
130                 }
131             }
132 
133         }
134     }
135 
136     // A DXF pattern element of exactly 0.0 is a *dot* (a point), distinct from a
137     // positive-length dash (e.g. DOT2 = 0,-3.175). AutoCAD paints it as a small
138     // dot, but VCL has no round dot: LineInfo strokes every "on" run as a short
139     // segment, so a dot is just a very short dash. Rendered verbatim the length
140     // collapses to a sub-unit mark (Transform() only clamps it to 1/100mm) and the
141     // line came out invisible (issue 70275). Give a zero-length dot/dash a small
142     // model-unit length so it survives scaling; keep it a SMALL fraction of the
143     // gap (0.05) so it reads as a dot, not a dash. Done here in model units, not
144     // after scaling, so it tracks the drawing size. Only true 0.0 elements are
145     // adjusted — real authored dashes keep their length.
146     const double fDotOnFraction = 0.05;   // dot mark length as a fraction of the gap
147     if (aDXFLineInfo.fDistance != 0.0) {
148         if (aDXFLineInfo.nDotCount > 0 && aDXFLineInfo.fDotLen == 0.0)
149             aDXFLineInfo.fDotLen = aDXFLineInfo.fDistance * fDotOnFraction;
150         if (aDXFLineInfo.nDashCount > 0 && aDXFLineInfo.fDashLen == 0.0)
151             aDXFLineInfo.fDashLen = aDXFLineInfo.fDistance * fDotOnFraction;
152     }
153 
154     return aDXFLineInfo;
155 }
156 
GetEntityDXFLineInfo(const DXFBasicEntity & rE)157 DXFLineInfo DXF2GDIMetaFile::GetEntityDXFLineInfo(const DXFBasicEntity & rE)
158 {
159     DXFLineInfo aDXFLineInfo;
160     const DXFLayer * pLayer;
161 
162     aDXFLineInfo.eStyle = LINE_SOLID;
163     aDXFLineInfo.fWidth = 0;
164     aDXFLineInfo.nDashCount = 0;
165     aDXFLineInfo.fDashLen = 0;
166     aDXFLineInfo.nDotCount = 0;
167     aDXFLineInfo.fDotLen = 0;
168     aDXFLineInfo.fDistance = 0;
169 
170     if (strcmp(rE.sLineType,"BYLAYER")==0) {
171         if (rE.sLayer[0]=='0' && rE.sLayer[1]==0) aDXFLineInfo=aParentLayerDXFLineInfo;
172         else {
173             pLayer=pDXF->aTables.SearchLayer(rE.sLayer);
174             if (pLayer!=NULL) aDXFLineInfo=LTypeToDXFLineInfo(pLayer->sLineType);
175             else aDXFLineInfo=aParentLayerDXFLineInfo;
176         }
177     }
178     else if (strcmp(rE.sLineType,"BYBLOCK")==0) {
179         aDXFLineInfo=aBlockDXFLineInfo;
180     }
181     else aDXFLineInfo=LTypeToDXFLineInfo(rE.sLineType);
182     return aDXFLineInfo;
183 }
184 
185 
SetLineAttribute(const DXFBasicEntity & rE,sal_uLong)186 sal_Bool DXF2GDIMetaFile::SetLineAttribute(const DXFBasicEntity & rE, sal_uLong /*nWidth*/)
187 {
188     long nColor;
189     Color aColor;
190 
191     nColor=GetEntityColor(rE);
192     if (nColor<0) return sal_False;
193     aColor=ConvertColor((sal_uInt8)nColor);
194 
195     if (aActLineColor!=aColor) {
196         pVirDev->SetLineColor( aActLineColor = aColor );
197     }
198 
199     if (aActFillColor!=Color( COL_TRANSPARENT )) {
200         pVirDev->SetFillColor(aActFillColor = Color( COL_TRANSPARENT ));
201     }
202     return sal_True;
203 }
204 
205 
SetAreaAttribute(const DXFBasicEntity & rE)206 sal_Bool DXF2GDIMetaFile::SetAreaAttribute(const DXFBasicEntity & rE)
207 {
208     long nColor;
209     Color aColor;
210 
211     nColor=GetEntityColor(rE);
212     if (nColor<0) return sal_False;
213     aColor=ConvertColor((sal_uInt8)nColor);
214 
215     if (aActLineColor!=aColor) {
216         pVirDev->SetLineColor( aActLineColor = aColor );
217     }
218 
219     if ( aActFillColor == Color( COL_TRANSPARENT ) || aActFillColor != aColor) {
220         pVirDev->SetFillColor( aActFillColor = aColor );
221     }
222     return sal_True;
223 }
224 
225 
SetFontAttribute(const DXFBasicEntity & rE,short nAngle,sal_uInt16 nHeight,double)226 sal_Bool DXF2GDIMetaFile::SetFontAttribute(const DXFBasicEntity & rE, short nAngle, sal_uInt16 nHeight, double /*fWidthScale*/)
227 {
228     long nColor;
229     Color aColor;
230     Font aFont;
231 
232     nAngle=-nAngle;
233     while (nAngle>3600) nAngle-=3600;
234     while (nAngle<0) nAngle+=3600;
235 
236     nColor=GetEntityColor(rE);
237     if (nColor<0) return sal_False;
238     aColor=ConvertColor((sal_uInt8)nColor);
239 
240     aFont.SetColor(aColor);
241     aFont.SetTransparent(sal_True);
242     aFont.SetFamily(FAMILY_SWISS);
243     aFont.SetSize(Size(0,nHeight));
244     aFont.SetAlign(ALIGN_BASELINE);
245     aFont.SetOrientation(nAngle);
246     if (aActFont!=aFont) {
247         aActFont=aFont;
248         pVirDev->SetFont(aActFont);
249     }
250 
251     return sal_True;
252 }
253 
254 
DrawLineEntity(const DXFLineEntity & rE,const DXFTransform & rTransform)255 void DXF2GDIMetaFile::DrawLineEntity(const DXFLineEntity & rE, const DXFTransform & rTransform)
256 {
257     if (SetLineAttribute(rE)) {
258         Point aP0,aP1;
259         rTransform.Transform(rE.aP0,aP0);
260         rTransform.Transform(rE.aP1,aP1);
261 
262         DXFLineInfo aDXFLineInfo;
263         aDXFLineInfo=GetEntityDXFLineInfo(rE);
264         LineInfo aLineInfo;
265         aLineInfo = rTransform.Transform(aDXFLineInfo);
266 
267 #if 0
268         printf("%f\n", rTransform.TransLineWidth(1000.0));
269 
270         // LINE_NONE = 0, LINE_SOLID = 1, LINE_DASH = 2, LineStyle_FORCE_EQUAL_SIZE = SAL_MAX_ENUM
271         aLineInfo.SetStyle( LINE_DASH );
272         aLineInfo.SetWidth( 300 );
273         aLineInfo.SetDashCount( 2 );
274         aLineInfo.SetDashLen( 100 );
275         aLineInfo.SetDotCount( 1 );
276         aLineInfo.SetDotLen( 0 );
277         aLineInfo.SetDistance( 500 );
278 #endif
279 
280         pVirDev->DrawLine(aP0,aP1,aLineInfo);
281         if (rE.fThickness!=0) {
282             Point aP2,aP3;
283             rTransform.Transform(rE.aP0+DXFVector(0,0,rE.fThickness),aP2);
284             rTransform.Transform(rE.aP1+DXFVector(0,0,rE.fThickness),aP3);
285             pVirDev->DrawLine(aP2,aP3);
286             pVirDev->DrawLine(aP0,aP2);
287             pVirDev->DrawLine(aP1,aP3);
288         }
289     }
290 }
291 
292 
DrawPointEntity(const DXFPointEntity & rE,const DXFTransform & rTransform)293 void DXF2GDIMetaFile::DrawPointEntity(const DXFPointEntity & rE, const DXFTransform & rTransform)
294 {
295 
296     if (SetLineAttribute(rE)) {
297         Point aP0;
298         rTransform.Transform(rE.aP0,aP0);
299         if (rE.fThickness==0) pVirDev->DrawPixel(aP0);
300         else {
301             Point aP1;
302             rTransform.Transform(rE.aP0+DXFVector(0,0,rE.fThickness),aP1);
303             pVirDev->DrawLine(aP0,aP1);
304         }
305     }
306 }
307 
308 
DrawCircleEntity(const DXFCircleEntity & rE,const DXFTransform & rTransform)309 void DXF2GDIMetaFile::DrawCircleEntity(const DXFCircleEntity & rE, const DXFTransform & rTransform)
310 {
311     double frx,fry,fAng;
312     sal_uInt16 nPoints,i;
313     DXFVector aC;
314 
315     if (SetLineAttribute(rE)==sal_False) return;
316     rTransform.Transform(rE.aP0,aC);
317     if (rE.fThickness==0 && rTransform.TransCircleToEllipse(rE.fRadius,frx,fry)==sal_True) {
318         pVirDev->DrawEllipse(
319             Rectangle((long)(aC.fx-frx+0.5),(long)(aC.fy-fry+0.5),
320                       (long)(aC.fx+frx+0.5),(long)(aC.fy+fry+0.5)));
321     }
322     else {
323         nPoints=OptPointsPerCircle;
324         Polygon aPoly(nPoints);
325         for (i=0; i<nPoints; i++) {
326             fAng=2*3.14159265359/(double)(nPoints-1)*(double)i;
327             rTransform.Transform(
328                 rE.aP0+DXFVector(rE.fRadius*cos(fAng),rE.fRadius*sin(fAng),0),
329                 aPoly[i]
330             );
331         }
332         pVirDev->DrawPolyLine(aPoly);
333         if (rE.fThickness!=0) {
334             Polygon aPoly2(nPoints);
335             for (i=0; i<nPoints; i++) {
336                 fAng=2*3.14159265359/(double)(nPoints-1)*(double)i;
337                 rTransform.Transform(
338                     rE.aP0+DXFVector(rE.fRadius*cos(fAng),rE.fRadius*sin(fAng),rE.fThickness),
339                     aPoly2[i]
340                 );
341 
342             }
343             pVirDev->DrawPolyLine(aPoly2);
344             for (i=0; i<nPoints-1; i++) pVirDev->DrawLine(aPoly[i],aPoly2[i]);
345         }
346     }
347 }
348 
349 
DrawArcEntity(const DXFArcEntity & rE,const DXFTransform & rTransform)350 void DXF2GDIMetaFile::DrawArcEntity(const DXFArcEntity & rE, const DXFTransform & rTransform)
351 {
352     double frx,fry,fA1,fdA,fAng;
353     sal_uInt16 nPoints,i;
354     DXFVector aC;
355     Point aPS,aPE;
356 
357     if (SetLineAttribute(rE)==sal_False) return;
358     fA1=rE.fStart;
359     fdA=rE.fEnd-fA1;
360     while (fdA>=360.0) fdA-=360.0;
361     while (fdA<=0) fdA+=360.0;
362     rTransform.Transform(rE.aP0,aC);
363     if (rE.fThickness==0 && fdA>5.0 && rTransform.TransCircleToEllipse(rE.fRadius,frx,fry)==sal_True) {
364         DXFVector aVS(cos(fA1/180.0*3.14159265359),sin(fA1/180.0*3.14159265359),0.0);
365         aVS*=rE.fRadius;
366         aVS+=rE.aP0;
367         DXFVector aVE(cos((fA1+fdA)/180.0*3.14159265359),sin((fA1+fdA)/180.0*3.14159265359),0.0);
368         aVE*=rE.fRadius;
369         aVE+=rE.aP0;
370         if (rTransform.Mirror()==sal_True) {
371             rTransform.Transform(aVS,aPS);
372             rTransform.Transform(aVE,aPE);
373         }
374         else {
375             rTransform.Transform(aVS,aPE);
376             rTransform.Transform(aVE,aPS);
377         }
378         pVirDev->DrawArc(
379             Rectangle((long)(aC.fx-frx+0.5),(long)(aC.fy-fry+0.5),
380                       (long)(aC.fx+frx+0.5),(long)(aC.fy+fry+0.5)),
381             aPS,aPE
382         );
383     }
384     else {
385         nPoints=(sal_uInt16)(fdA/360.0*(double)OptPointsPerCircle+0.5);
386         if (nPoints<2) nPoints=2;
387         Polygon aPoly(nPoints);
388         for (i=0; i<nPoints; i++) {
389             fAng=3.14159265359/180.0 * ( fA1 + fdA/(double)(nPoints-1)*(double)i );
390             rTransform.Transform(
391                 rE.aP0+DXFVector(rE.fRadius*cos(fAng),rE.fRadius*sin(fAng),0),
392                 aPoly[i]
393             );
394         }
395         pVirDev->DrawPolyLine(aPoly);
396         if (rE.fThickness!=0) {
397             Polygon aPoly2(nPoints);
398             for (i=0; i<nPoints; i++) {
399                 fAng=3.14159265359/180.0 * ( fA1 + fdA/(double)(nPoints-1)*(double)i );
400                 rTransform.Transform(
401                     rE.aP0+DXFVector(rE.fRadius*cos(fAng),rE.fRadius*sin(fAng),rE.fThickness),
402                     aPoly2[i]
403                 );
404             }
405             pVirDev->DrawPolyLine(aPoly2);
406             for (i=0; i<nPoints; i++) pVirDev->DrawLine(aPoly[i],aPoly2[i]);
407         }
408     }
409 }
410 
411 
DrawTraceEntity(const DXFTraceEntity & rE,const DXFTransform & rTransform)412 void DXF2GDIMetaFile::DrawTraceEntity(const DXFTraceEntity & rE, const DXFTransform & rTransform)
413 {
414     if (SetLineAttribute(rE)) {
415         Polygon aPoly(4);
416         rTransform.Transform(rE.aP0,aPoly[0]);
417         rTransform.Transform(rE.aP1,aPoly[1]);
418         rTransform.Transform(rE.aP3,aPoly[2]);
419         rTransform.Transform(rE.aP2,aPoly[3]);
420         pVirDev->DrawPolygon(aPoly);
421         if (rE.fThickness!=0) {
422             sal_uInt16 i;
423             Polygon aPoly2(4);
424             DXFVector aVAdd(0,0,rE.fThickness);
425             rTransform.Transform(rE.aP0+aVAdd,aPoly2[0]);
426             rTransform.Transform(rE.aP1+aVAdd,aPoly2[1]);
427             rTransform.Transform(rE.aP3+aVAdd,aPoly2[2]);
428             rTransform.Transform(rE.aP2+aVAdd,aPoly2[3]);
429             pVirDev->DrawPolygon(aPoly2);
430             for (i=0; i<4; i++) pVirDev->DrawLine(aPoly[i],aPoly2[i]);
431         }
432     }
433 }
434 
435 
DrawSolidEntity(const DXFSolidEntity & rE,const DXFTransform & rTransform)436 void DXF2GDIMetaFile::DrawSolidEntity(const DXFSolidEntity & rE, const DXFTransform & rTransform)
437 {
438     if (SetAreaAttribute(rE)) {
439         sal_uInt16 nN;
440         if (rE.aP2==rE.aP3) nN=3; else nN=4;
441         Polygon aPoly(nN);
442         rTransform.Transform(rE.aP0,aPoly[0]);
443         rTransform.Transform(rE.aP1,aPoly[1]);
444         rTransform.Transform(rE.aP3,aPoly[2]);
445         if (nN>3) rTransform.Transform(rE.aP2,aPoly[3]);
446         pVirDev->DrawPolygon(aPoly);
447         if (rE.fThickness!=0) {
448             Polygon aPoly2(nN);
449             DXFVector aVAdd(0,0,rE.fThickness);
450             rTransform.Transform(rE.aP0+aVAdd,aPoly2[0]);
451             rTransform.Transform(rE.aP1+aVAdd,aPoly2[1]);
452             rTransform.Transform(rE.aP3+aVAdd,aPoly2[2]);
453             if (nN>3) rTransform.Transform(rE.aP2+aVAdd,aPoly2[3]);
454             pVirDev->DrawPolygon(aPoly2);
455             if (SetLineAttribute(rE)) {
456                 sal_uInt16 i;
457                 for (i=0; i<nN; i++) pVirDev->DrawLine(aPoly[i],aPoly2[i]);
458             }
459         }
460     }
461 }
462 
463 
DrawTextEntity(const DXFTextEntity & rE,const DXFTransform & rTransform)464 void DXF2GDIMetaFile::DrawTextEntity(const DXFTextEntity & rE, const DXFTransform & rTransform)
465 {
466     DXFVector aV;
467     Point aPt;
468     double fA;
469     sal_uInt16 nHeight;
470     short nAng;
471     ByteString  aStr( rE.sText );
472     DXFTransform aT( DXFTransform(rE.fXScale,rE.fHeight,1.0,rE.fRotAngle,rE.aP0), rTransform );
473     aT.TransDir(DXFVector(0,1,0),aV);
474     nHeight=(sal_uInt16)(aV.Abs()+0.5);
475     fA=aT.CalcRotAngle();
476     nAng=(short)(fA*10.0+0.5);
477     aT.TransDir(DXFVector(1,0,0),aV);
478     if ( SetFontAttribute( rE,nAng, nHeight, aV. Abs() ) )
479     {
480         String aUString( aStr, pDXF->getTextEncoding() );
481         aT.Transform( DXFVector( 0, 0, 0 ), aPt );
482         pVirDev->DrawText( aPt, aUString );
483     }
484 }
485 
486 
487 // Draw a TEXT entity projected into the (3D) view, used when the drawing is
488 // rendered through a non-top viewport. VCL's DrawText can only place text with a
489 // position + uniform height + single rotation, so it cannot follow the shear /
490 // foreshortening a 3D projection produces. Instead we take the glyph OUTLINES and
491 // push them through the full transform like any other geometry, so the text lies
492 // in its projected plane. The flat 2D path (DrawTextEntity) is left untouched.
Draw3DTextEntity(const DXFTextEntity & rE,const DXFTransform & rTransform)493 void DXF2GDIMetaFile::Draw3DTextEntity(const DXFTextEntity & rE, const DXFTransform & rTransform)
494 {
495     ByteString aStr( rE.sText );
496     String aUString( aStr, pDXF->getTextEncoding() );
497     if ( aUString.Len()==0 ) return;
498 
499     long nColor = GetEntityColor(rE);
500     if ( nColor<0 ) return;
501     Color aColor = ConvertColor((sal_uInt8)nColor);
502 
503     // Extract the glyph outlines from a SCRATCH device, never from pVirDev: the
504     // recording device has output disabled, so its GetTextOutlines falls back to a
505     // bitmap path that fails ("could not create system bitmap"). A clean scalable
506     // font on a plain VirtualDevice takes the vector path. The nominal height is
507     // arbitrary — the extent is self-calibrated below.
508     const long nNominal = 1000;
509     Font aFont;
510     aFont.SetFamily(FAMILY_SWISS);
511     aFont.SetSize(Size(0,nNominal));
512     aFont.SetAlign(ALIGN_BASELINE);
513     VirtualDevice aTextDev;
514     aTextDev.SetFont(aFont);
515     PolyPolyVector aGlyphs;
516     sal_Bool bOK = aTextDev.GetTextOutlines( aGlyphs, aUString );
517     if ( !bOK || aGlyphs.empty() ) {
518         DrawTextEntity(rE,rTransform);   // no outlines available -> flat fallback
519         return;
520     }
521 
522     // GetTextOutlines returns the glyphs in an opaque internal scale (it toggles
523     // the map mode and rescales by a pixel factor), so we can't assume the height
524     // is nNominal. Self-calibrate: measure the real outline extent and normalise by
525     // its height, so one text-height maps to 1.0 regardless of the device scale.
526     double fMinY=0.0, fMaxY=0.0;
527     sal_Bool bFirst = sal_True;
528     sal_uInt32 g;
529     for ( g=0; g<aGlyphs.size(); g++ ) {
530         const PolyPolygon & rGlyph = aGlyphs[ g ];
531         sal_uInt16 c;
532         for ( c=0; c<rGlyph.Count(); c++ ) {
533             const Polygon & rContour = rGlyph[ c ];
534             sal_uInt16 i, nPts = rContour.GetSize();
535             for ( i=0; i<nPts; i++ ) {
536                 const double y = (double)rContour[ i ].Y();
537                 if ( bFirst ) { fMinY=fMaxY=y; bFirst=sal_False; }
538                 else { if (y<fMinY) fMinY=y; if (y>fMaxY) fMaxY=y; }
539             }
540         }
541     }
542     const double fHeight = fMaxY - fMinY;
543     if ( fHeight <= 0.0 ) {          // degenerate outlines -> flat fallback
544         DrawTextEntity(rE,rTransform);
545         return;
546     }
547     const double fInv = 1.0/fHeight;
548 
549     // Text-local coords (baseline at Y=0, one text-height == 1.0, Y up) -> DXF
550     // text placement (width/height/rotation/insertion) -> the incoming extrusion+
551     // view transform. This is the same chain the renderer uses for geometry.
552     // The outlines are normalised to text-height units in BOTH axes, so X and Y
553     // must both scale by fHeight; fXScale (DXF group 41 width factor) is only an
554     // extra horizontal stretch on top (default 1.0). Scaling X by fXScale alone —
555     // as DrawTextEntity can, because it re-derives width from the font — would
556     // collapse the glyphs to a vertical sliver here.
557     const double fWScale = rE.fHeight * ( rE.fXScale>0.0 ? rE.fXScale : 1.0 );
558     DXFTransform aT( DXFTransform(fWScale,rE.fHeight,1.0,rE.fRotAngle,rE.aP0), rTransform );
559 
560     if ( aActFillColor!=aColor ) pVirDev->SetFillColor( aActFillColor = aColor );
561     if ( aActLineColor!=aColor ) pVirDev->SetLineColor( aActLineColor = aColor );
562 
563     for ( g=0; g<aGlyphs.size(); g++ ) {
564         const PolyPolygon & rGlyph = aGlyphs[ g ];
565         PolyPolygon aDevGlyph;
566         sal_uInt16 c;
567         for ( c=0; c<rGlyph.Count(); c++ ) {
568             const Polygon & rContour = rGlyph[ c ];
569             sal_uInt16 nPts = rContour.GetSize();
570             Polygon aDev( nPts );
571             sal_uInt16 i;
572             for ( i=0; i<nPts; i++ ) {
573                 const Point & rP = rContour[ i ];
574                 aT.Transform( DXFVector( (double)rP.X()*fInv,
575                                          -(double)rP.Y()*fInv, 0.0 ),
576                               aDev[ i ] );
577             }
578             aDevGlyph.Insert( aDev );
579         }
580         pVirDev->DrawPolyPolygon( aDevGlyph );
581     }
582 }
583 
584 
DrawInsertEntity(const DXFInsertEntity & rE,const DXFTransform & rTransform)585 void DXF2GDIMetaFile::DrawInsertEntity(const DXFInsertEntity & rE, const DXFTransform & rTransform)
586 {
587     const DXFBlock * pB;
588     pB=pDXF->aBlocks.Search(rE.sName);
589     if (pB!=NULL) {
590         DXFTransform aDXFTransform1(1.0,1.0,1.0,DXFVector(0.0,0.0,0.0)-pB->aBasePoint);
591         DXFTransform aDXFTransform2(rE.fXScale,rE.fYScale,rE.fZScale,rE.fRotAngle,rE.aP0);
592         DXFTransform aT(
593             DXFTransform( aDXFTransform1, aDXFTransform2 ),
594             rTransform
595         );
596         long nSavedBlockColor, nSavedParentLayerColor;
597         DXFLineInfo aSavedBlockDXFLineInfo, aSavedParentLayerDXFLineInfo;
598         nSavedBlockColor=nBlockColor;
599         nSavedParentLayerColor=nParentLayerColor;
600         aSavedBlockDXFLineInfo=aBlockDXFLineInfo;
601         aSavedParentLayerDXFLineInfo=aParentLayerDXFLineInfo;
602         nBlockColor=GetEntityColor(rE);
603         aBlockDXFLineInfo=GetEntityDXFLineInfo(rE);
604         if (rE.sLayer[0]!='0' || rE.sLayer[1]!=0) {
605             DXFLayer * pLayer=pDXF->aTables.SearchLayer(rE.sLayer);
606             if (pLayer!=NULL) {
607                 nParentLayerColor=pLayer->nColor;
608                 aParentLayerDXFLineInfo=LTypeToDXFLineInfo(pLayer->sLineType);
609             }
610         }
611         DrawEntities(*pB,aT,sal_False);
612         aBlockDXFLineInfo=aSavedBlockDXFLineInfo;
613         aParentLayerDXFLineInfo=aSavedParentLayerDXFLineInfo;
614         nBlockColor=nSavedBlockColor;
615         nParentLayerColor=nSavedParentLayerColor;
616     }
617 }
618 
619 
DrawAttribEntity(const DXFAttribEntity & rE,const DXFTransform & rTransform)620 void DXF2GDIMetaFile::DrawAttribEntity(const DXFAttribEntity & rE, const DXFTransform & rTransform)
621 {
622     if ((rE.nAttrFlags&1)==0) {
623         DXFVector aV;
624         Point aPt;
625         double fA;
626         sal_uInt16 nHeight;
627         short nAng;
628         ByteString aStr( rE.sText );
629         DXFTransform aT( DXFTransform( rE.fXScale, rE.fHeight, 1.0, rE.fRotAngle, rE.aP0 ), rTransform );
630         aT.TransDir(DXFVector(0,1,0),aV);
631         nHeight=(sal_uInt16)(aV.Abs()+0.5);
632         fA=aT.CalcRotAngle();
633         nAng=(short)(fA*10.0+0.5);
634         aT.TransDir(DXFVector(1,0,0),aV);
635         if (SetFontAttribute(rE,nAng,nHeight,aV.Abs()))
636         {
637             String aUString( aStr, pDXF->getTextEncoding() );
638             aT.Transform( DXFVector( 0, 0, 0 ), aPt );
639             pVirDev->DrawText( aPt, aUString );
640         }
641     }
642 }
643 
644 
DrawPolyLineEntity(const DXFPolyLineEntity & rE,const DXFTransform & rTransform)645 void DXF2GDIMetaFile::DrawPolyLineEntity(const DXFPolyLineEntity & rE, const DXFTransform & rTransform)
646 {
647     sal_uInt16 i,nPolySize;
648     double fW;
649     const DXFBasicEntity * pBE;
650 
651     nPolySize=0;
652     pBE=rE.pSucc;
653     while (pBE!=NULL && pBE->eType==DXF_VERTEX) {
654         nPolySize++;
655         pBE=pBE->pSucc;
656     }
657     if (nPolySize<2) return;
658     Polygon aPoly(nPolySize);
659     fW=0.0;
660     pBE=rE.pSucc;
661     for (i=0; i<nPolySize; i++) {
662         rTransform.Transform(((DXFVertexEntity*)pBE)->aP0,aPoly[i]);
663         if (i+1<nPolySize || (rE.nFlags&1)!=0) {
664             if (((DXFVertexEntity*)pBE)->fSWidth>=0.0) fW+=((DXFVertexEntity*)pBE)->fSWidth;
665             else fW+=rE.fSWidth;
666             if (((DXFVertexEntity*)pBE)->fEWidth>=0.0) fW+=((DXFVertexEntity*)pBE)->fEWidth;
667             else fW+=rE.fEWidth;
668         }
669         pBE=pBE->pSucc;
670     }
671     fW/=2.0;
672     if ((rE.nFlags&1)!=0) fW/=(double)nPolySize;
673     else fW/=(double)(nPolySize-1);
674     if (SetLineAttribute(rE,rTransform.TransLineWidth(fW))) {
675         if ((rE.nFlags&1)!=0) pVirDev->DrawPolygon(aPoly);
676         else pVirDev->DrawPolyLine(aPoly);
677         if (rE.fThickness!=0) {
678             Polygon aPoly2(nPolySize);
679             pBE=rE.pSucc;
680             for (i=0; i<nPolySize; i++) {
681                 rTransform.Transform(
682                    (((DXFVertexEntity*)pBE)->aP0)+DXFVector(0,0,rE.fThickness),
683                    aPoly2[i]
684                 );
685                 pBE=pBE->pSucc;
686             }
687             if ((rE.nFlags&1)!=0) pVirDev->DrawPolygon(aPoly2);
688             else pVirDev->DrawPolyLine(aPoly2);
689             for (i=0; i<nPolySize; i++) pVirDev->DrawLine(aPoly[i],aPoly2[i]);
690         }
691     }
692 }
693 
DrawLWPolyLineEntity(const DXFLWPolyLineEntity & rE,const DXFTransform & rTransform)694 void DXF2GDIMetaFile::DrawLWPolyLineEntity(const DXFLWPolyLineEntity & rE, const DXFTransform & rTransform )
695 {
696     const sal_Int32 nVtxCount = rE.nCount;
697     if ( nVtxCount <= 0 || rE.pP == NULL )
698         return;
699 
700     double fW = rE.fConstantWidth;
701     if ( !SetLineAttribute( rE, rTransform.TransLineWidth( fW ) ) )
702         return;
703 
704     const sal_Bool bClosed = ( rE.nFlags & 1 ) != 0;
705 
706     // Build the outline in device space, expanding any segment that carries a
707     // non-zero bulge into an arc. In DXF a bulge is tan(sweep/4), so bulge 1.0
708     // is a 180 degree arc; a circle is commonly stored as two bulged segments
709     // (e.g. Eagle CAD). Straight segments (bulge 0) contribute just the vertex.
710     DXFPointArray aPtAry;
711     Point aPt;
712     for ( sal_Int32 i = 0; i < nVtxCount; i++ )
713     {
714         rTransform.Transform( rE.pP[ i ], aPt );
715         aPtAry.push_back( aPt );
716 
717         sal_Int32 iNext = i + 1;
718         if ( iNext >= nVtxCount )
719         {
720             if ( !bClosed ) break;   // open: no segment after the last vertex
721             iNext = 0;               // closed: last vertex arcs back to the first
722         }
723 
724         const double fBulge = ( rE.pBulge != NULL ) ? rE.pBulge[ i ] : 0.0;
725         if ( fBulge != 0.0 )
726         {
727             const DXFVector & rS = rE.pP[ i ];
728             const DXFVector & rEnd = rE.pP[ iNext ];
729             // Arc centre from the bulge (cotangent form): the centre lies on the
730             // chord's perpendicular bisector, offset by cot(sweep/2)/2 * chord.
731             const double fCot = 0.5 * ( 1.0 / fBulge - fBulge );
732             const double fCx = 0.5 * ( ( rS.fx + rEnd.fx ) + fCot * ( rS.fy - rEnd.fy ) );
733             const double fCy = 0.5 * ( ( rS.fy + rEnd.fy ) + fCot * ( rEnd.fx - rS.fx ) );
734             const double fRx = rS.fx - fCx;
735             const double fRy = rS.fy - fCy;
736             const double fRadius = sqrt( fRx * fRx + fRy * fRy );
737             const double fStartAng = atan2( fRy, fRx );
738             const double fSweep = 4.0 * atan( fBulge );   // signed: >0 = CCW
739             sal_Int32 nArcPoints = (sal_Int32)( fabs( fSweep ) / ( 2 * 3.14159265359 )
740                                                 * (double)OptPointsPerCircle + 0.5 );
741             // Emit the interior arc samples; the start vertex is already pushed
742             // and the end vertex is pushed as the next loop's leading vertex.
743             for ( sal_Int32 k = 1; k < nArcPoints; k++ )
744             {
745                 const double fAng = fStartAng + fSweep * (double)k / (double)nArcPoints;
746                 rTransform.Transform(
747                     DXFVector( fCx + fRadius * cos( fAng ),
748                                fCy + fRadius * sin( fAng ),
749                                rS.fz ),
750                     aPt );
751                 aPtAry.push_back( aPt );
752             }
753         }
754     }
755 
756     const sal_Int32 nPolySize = (sal_Int32)aPtAry.size();
757     if ( nPolySize < 2 )
758         return;
759 
760     if ( nPolySize <= 0xFFFF )
761     {
762         // Fits in a single tools Polygon (which is indexed by sal_uInt16).
763         Polygon aPoly( (sal_uInt16)nPolySize );
764         for ( sal_Int32 i = 0; i < nPolySize; i++ )
765             aPoly[ (sal_uInt16)i ] = aPtAry[ i ];
766         if ( bClosed )
767             pVirDev->DrawPolygon( aPoly );
768         else
769             pVirDev->DrawPolyLine( aPoly );
770         return;
771     }
772 
773     // The tessellated outline can exceed the 0xFFFF points a tools Polygon holds.
774     // Render it as a sequence of Polygon segments that overlap by one point, so
775     // the whole line is drawn without truncation and the stroke stays continuous.
776     const sal_Int32 nMaxChunk = 0xFFFF;
777     sal_Int32 nStart = 0;
778     while ( nStart + 1 < nPolySize )
779     {
780         sal_Int32 nEnd = nStart + nMaxChunk;
781         if ( nEnd > nPolySize )
782             nEnd = nPolySize;
783         const sal_uInt16 nChunk = (sal_uInt16)( nEnd - nStart );
784         Polygon aChunk( nChunk );
785         for ( sal_uInt16 j = 0; j < nChunk; j++ )
786             aChunk[ j ] = aPtAry[ nStart + j ];
787         pVirDev->DrawPolyLine( aChunk );
788         nStart = nEnd - 1;   // next segment shares this segment's last point
789     }
790     if ( bClosed )
791     {
792         // Close the outline: last point back to the first.
793         Polygon aClose( 2 );
794         aClose[ 0 ] = aPtAry[ nPolySize - 1 ];
795         aClose[ 1 ] = aPtAry[ 0 ];
796         pVirDev->DrawPolyLine( aClose );
797     }
798 }
799 
DrawHatchEntity(const DXFHatchEntity & rE,const DXFTransform & rTransform)800 void DXF2GDIMetaFile::DrawHatchEntity(const DXFHatchEntity & rE, const DXFTransform & rTransform )
801 {
802     if ( !rE.nBoundaryPathCount )
803         return;
804 
805     // Build the boundary as a PolyPolygon, tessellating curved edges so the
806     // area is measured correctly for both the solid fill and the pattern clip.
807     sal_Int32 j;
808     PolyPolygon aPolyPoly;
809     for ( j = 0; j < rE.nBoundaryPathCount; j++ )
810     {
811         DXFPointArray aPtAry;
812         const DXFBoundaryPathData& rPathData = rE.pBoundaryPathData[ j ];
813         if ( rPathData.bIsPolyLine )
814         {
815             sal_Int32 i;
816             for( i = 0; i < rPathData.nPointCount; i++ )
817             {
818                 Point aPt;
819                 rTransform.Transform( rPathData.pP[ i ], aPt );
820                 aPtAry.push_back( aPt );
821             }
822         }
823         else
824         {
825             sal_uInt32 i;
826             for ( i = 0; i < rPathData.aEdges.size(); i++ )
827             {
828                 const DXFEdgeType* pEdge = rPathData.aEdges[ i ];
829                 switch( pEdge->nEdgeType )
830                 {
831                     case 1 :
832                     {
833                         Point aPt;
834                         rTransform.Transform( ((DXFEdgeTypeLine*)pEdge)->aStartPoint, aPt );
835                         aPtAry.push_back( aPt );
836                         rTransform.Transform( ((DXFEdgeTypeLine*)pEdge)->aEndPoint, aPt );
837                         aPtAry.push_back( aPt );
838                     }
839                     break;
840                     case 2 :
841                     {
842                         // Circular arc edge: sample the sweep and push the points.
843                         // Angles are in degrees; direction follows the CCW flag.
844                         const DXFEdgeTypeCircularArc* pArc = (const DXFEdgeTypeCircularArc*)pEdge;
845                         double fSweep = pArc->fEndAngle - pArc->fStartAngle;
846                         if ( pArc->nIsCounterClockwiseFlag )
847                             { while ( fSweep <= 0.0 ) fSweep += 360.0; }
848                         else
849                             { while ( fSweep >= 0.0 ) fSweep -= 360.0; }
850                         sal_uInt16 nPts = (sal_uInt16)( fabs(fSweep)/360.0*(double)OptPointsPerCircle + 0.5 );
851                         if ( nPts < 2 ) nPts = 2;
852                         for ( sal_uInt16 k = 0; k < nPts; k++ )
853                         {
854                             double a = ( pArc->fStartAngle + fSweep*(double)k/(double)(nPts-1) )
855                                         * (3.14159265359/180.0);
856                             Point aPt;
857                             rTransform.Transform(
858                                 DXFVector( pArc->aCenter.fx + pArc->fRadius*cos(a),
859                                            pArc->aCenter.fy + pArc->fRadius*sin(a), 0.0 ), aPt );
860                             aPtAry.push_back( aPt );
861                         }
862                     }
863                     break;
864                     case 3 :    // elliptical arc: not tessellated (not seen in
865                     case 4 :    // practice here); spline edges store no control
866                     break;      // points, so both fall through as before.
867                 }
868             }
869         }
870         sal_uInt16 i, nSize = (sal_uInt16)aPtAry.size();
871         if ( nSize )
872         {
873             Polygon aPoly( nSize );
874             for ( i = 0; i < nSize; i++ )
875                 aPoly[ i ] = aPtAry[ i ];
876             aPolyPoly.Insert( aPoly, POLYPOLY_APPEND );
877         }
878     }
879     if ( !aPolyPoly.Count() )
880         return;
881 
882     if ( rE.nFlags & 1 )
883     {
884         // Solid fill (group 70 bit 0): flood-fill the boundary in the entity colour.
885         SetAreaAttribute( rE );
886         pVirDev->DrawPolyPolygon( aPolyPoly );
887     }
888     else if ( rE.bHasPatternLine )
889     {
890         // Pattern fill: render the section lines with VCL's DrawHatch, which
891         // clips a line family to the boundary and records a MetaHatchAction.
892         // The DXF pattern is collapsed to a single/double/triple Hatch; the
893         // on-page angle and spacing come from mapping the pattern-definition
894         // line's direction and inter-line offset through rTransform, so any
895         // scale / rotation / block-insert nesting is accounted for.
896         long nColor = GetEntityColor( rE );
897         if ( nColor < 0 )
898             return;
899         Color aColor = ConvertColor( (sal_uInt8)nColor );
900 
901         const double fPi = 3.14159265359;
902         double fA = rE.fPatternLineAngle * (fPi/180.0);
903         DXFVector aDir, aOff;
904         rTransform.TransDir( DXFVector( cos(fA), sin(fA), 0.0 ), aDir );
905         rTransform.TransDir( DXFVector( rE.fPatternLineOffsetX, rE.fPatternLineOffsetY, 0.0 ), aOff );
906 
907         double fDirLen = sqrt( aDir.fx*aDir.fx + aDir.fy*aDir.fy );
908         if ( fDirLen < 1e-9 )
909         {
910             // degenerate direction: fall back to a bare outline (no blob).
911             SetLineAttribute( rE );
912             pVirDev->DrawPolyPolygon( aPolyPoly );
913             return;
914         }
915 
916         // Perpendicular spacing between adjacent lines in device space, and the
917         // device line angle. VCL's angle is measured in the recorded coordinate
918         // system with its positive direction as (cos,-sin), hence -aDir.fy.
919         double fDist = fabs( aOff.fx*aDir.fy - aOff.fy*aDir.fx ) / fDirLen;
920         long nDistance = (long)( fDist + 0.5 );
921         if ( nDistance < 1 ) nDistance = 1;
922 
923         double fAngle = atan2( -aDir.fy, aDir.fx ) * 1800.0 / fPi;
924         long nAngle10 = (long)( fAngle + (fAngle < 0.0 ? -0.5 : 0.5) );
925         nAngle10 %= 1800;
926         if ( nAngle10 < 0 )
927             nAngle10 += 1800;
928 
929         HatchStyle eStyle = HATCH_SINGLE;
930         if ( rE.nHatchPatternDefinitionLines == 2 )
931             eStyle = HATCH_DOUBLE;
932         else if ( rE.nHatchPatternDefinitionLines >= 3 )
933             eStyle = HATCH_TRIPLE;
934 
935         Hatch aHatch( eStyle, aColor, nDistance, (sal_uInt16)nAngle10 );
936         pVirDev->DrawHatch( aPolyPoly, aHatch );
937     }
938     else
939     {
940         // Pattern fill but no pattern-definition line was parsed: stroke the
941         // boundary outline only (transparent fill), never a solid blob.
942         SetLineAttribute( rE );
943         pVirDev->DrawPolyPolygon( aPolyPoly );
944     }
945 }
946 
DrawEllipseEntity(const DXFEllipseEntity & rE,const DXFTransform & rTransform)947 void DXF2GDIMetaFile::DrawEllipseEntity(const DXFEllipseEntity & rE, const DXFTransform & rTransform)
948 {
949     if (SetLineAttribute(rE)==sal_False) return;
950 
951     // Major axis vector (relative to centre) and the minor axis, which for the
952     // common planar case (extrusion 0,0,1) is ratio*(normal x major) =
953     // ratio*(-major.y, major.x, 0). A non-default extrusion is applied through
954     // rTransform by DrawEntities, so compute in the entity's own frame.
955     const DXFVector & aU = rE.aP1;
956     DXFVector aV(-aU.fy * rE.fRatio, aU.fx * rE.fRatio, 0.0);
957 
958     double fSweep = rE.fEnd - rE.fStart;
959     while (fSweep <= 0.0)             fSweep += 2*3.14159265359;
960     while (fSweep >  2*3.14159265359) fSweep -= 2*3.14159265359;
961 
962     sal_uInt16 nPoints = (sal_uInt16)( fSweep/(2*3.14159265359)*(double)OptPointsPerCircle + 0.5 );
963     if (nPoints<2) nPoints=2;
964 
965     Polygon aPoly(nPoints);
966     for (sal_uInt16 i=0; i<nPoints; i++) {
967         double t = rE.fStart + fSweep*(double)i/(double)(nPoints-1);
968         rTransform.Transform( rE.aP0 + aU*cos(t) + aV*sin(t), aPoly[i] );
969     }
970     pVirDev->DrawPolyLine(aPoly);
971 }
972 
973 
DrawSplineEntity(const DXFSplineEntity & rE,const DXFTransform & rTransform)974 void DXF2GDIMetaFile::DrawSplineEntity(const DXFSplineEntity & rE, const DXFTransform & rTransform)
975 {
976     // Non-uniform B-spline, evaluated with de Boor's algorithm and drawn as a
977     // polyline. Rational (weighted) splines are treated as non-rational (the
978     // weights are ignored) — sufficient for the planar curves this targets.
979     if ( rE.pControlPts==NULL || rE.pfKnots==NULL ) return;
980     const long p = rE.nDegree;
981     const long n = rE.nCtrlCount - 1;
982     if ( p < 1 || n < p ) return;
983     if ( rE.nKnotCount != rE.nCtrlCount + p + 1 ) return;   // not a clamped B-spline
984     if ( SetLineAttribute(rE)==sal_False ) return;
985 
986     const double * U = rE.pfKnots;
987     const double u0 = U[p];
988     const double u1 = U[n+1];
989     if ( u1 <= u0 ) return;
990 
991     long nPoints = rE.nCtrlCount * 8;
992     if (nPoints < 2)    nPoints = 2;
993     if (nPoints > 2000) nPoints = 2000;
994 
995     Polygon aPoly((sal_uInt16)nPoints);
996     DXFVector * d = new DXFVector[p+1];
997     for (long i=0; i<nPoints; i++) {
998         double u = u0 + (u1-u0)*(double)i/(double)(nPoints-1);
999         if (u >= u1) u = u1 - (u1-u0)*1e-9;   // stay inside the last knot span
1000 
1001         // knot span k with U[k] <= u < U[k+1], clamped to [p, n]
1002         long k = p;
1003         while (k < n && u >= U[k+1]) k++;
1004 
1005         long j, r;
1006         for (j=0; j<=p; j++) d[j] = rE.pControlPts[k-p+j];
1007         for (r=1; r<=p; r++) {
1008             for (j=p; j>=r; j--) {
1009                 double fDenom = U[k+1+j-r] - U[k-p+j];
1010                 double a = (fDenom!=0.0) ? (u - U[k-p+j])/fDenom : 0.0;
1011                 d[j] = d[j-1]*(1.0-a) + d[j]*a;
1012             }
1013         }
1014         rTransform.Transform(d[p], aPoly[(sal_uInt16)i]);
1015     }
1016     delete[] d;
1017     pVirDev->DrawPolyLine(aPoly);
1018 }
1019 
1020 
Draw3DFaceEntity(const DXF3DFaceEntity & rE,const DXFTransform & rTransform)1021 void DXF2GDIMetaFile::Draw3DFaceEntity(const DXF3DFaceEntity & rE, const DXFTransform & rTransform)
1022 {
1023     sal_uInt16 nN,i;
1024     if (SetLineAttribute(rE)) {
1025         if (rE.aP2==rE.aP3) nN=3; else nN=4;
1026         Polygon aPoly(nN);
1027         rTransform.Transform(rE.aP0,aPoly[0]);
1028         rTransform.Transform(rE.aP1,aPoly[1]);
1029         rTransform.Transform(rE.aP2,aPoly[2]);
1030         if (nN>3) rTransform.Transform(rE.aP3,aPoly[3]);
1031         if ((rE.nIEFlags&0x0f)==0) pVirDev->DrawPolygon(aPoly);
1032         else {
1033             for (i=0; i<nN; i++) {
1034                 if ( (rE.nIEFlags & (1<<i)) == 0 ) {
1035                     pVirDev->DrawLine(aPoly[i],aPoly[(i+1)%nN]);
1036                 }
1037             }
1038         }
1039     }
1040 }
1041 
1042 
DrawDimensionEntity(const DXFDimensionEntity & rE,const DXFTransform & rTransform)1043 void DXF2GDIMetaFile::DrawDimensionEntity(const DXFDimensionEntity & rE, const DXFTransform & rTransform)
1044 {
1045     const DXFBlock * pB;
1046     pB=pDXF->aBlocks.Search(rE.sPseudoBlock);
1047     if (pB!=NULL) {
1048         DXFTransform aT(
1049             DXFTransform(1.0,1.0,1.0,DXFVector(0.0,0.0,0.0)-pB->aBasePoint),
1050             rTransform
1051         );
1052         long nSavedBlockColor, nSavedParentLayerColor;
1053         DXFLineInfo aSavedBlockDXFLineInfo, aSavedParentLayerDXFLineInfo;
1054         nSavedBlockColor=nBlockColor;
1055         nSavedParentLayerColor=nParentLayerColor;
1056         aSavedBlockDXFLineInfo=aBlockDXFLineInfo;
1057         aSavedParentLayerDXFLineInfo=aParentLayerDXFLineInfo;
1058         nBlockColor=GetEntityColor(rE);
1059         aBlockDXFLineInfo=GetEntityDXFLineInfo(rE);
1060         if (rE.sLayer[0]!='0' || rE.sLayer[1]!=0) {
1061             DXFLayer * pLayer=pDXF->aTables.SearchLayer(rE.sLayer);
1062             if (pLayer!=NULL) {
1063                 nParentLayerColor=pLayer->nColor;
1064                 aParentLayerDXFLineInfo=LTypeToDXFLineInfo(pLayer->sLineType);
1065             }
1066         }
1067         DrawEntities(*pB,aT,sal_False);
1068         aBlockDXFLineInfo=aSavedBlockDXFLineInfo;
1069         aParentLayerDXFLineInfo=aSavedParentLayerDXFLineInfo;
1070         nBlockColor=nSavedBlockColor;
1071         nParentLayerColor=nSavedParentLayerColor;
1072     }
1073 }
1074 
1075 
DrawEntities(const DXFEntities & rEntities,const DXFTransform & rTransform,sal_Bool bTopEntities)1076 void DXF2GDIMetaFile::DrawEntities(const DXFEntities & rEntities,
1077                                    const DXFTransform & rTransform,
1078                                    sal_Bool bTopEntities)
1079 {
1080     sal_uLong nCount=0;
1081     DXFTransform aET;
1082     const DXFTransform * pT;
1083 
1084     const DXFBasicEntity * pE=rEntities.pFirst;
1085 
1086     while (pE!=NULL && bStatus==sal_True) {
1087         if (pE->nSpace==0) {
1088             if (pE->aExtrusion.fz==1.0 || DXFCoordsAreWCS(*pE)) {
1089                 pT=&rTransform;
1090             }
1091             else {
1092                 aET=DXFTransform(DXFTransform(pE->aExtrusion),rTransform);
1093                 pT=&aET;
1094             }
1095             switch (pE->eType) {
1096             case DXF_LINE:
1097                 DrawLineEntity((DXFLineEntity&)*pE,*pT);
1098                 break;
1099             case DXF_POINT:
1100                 DrawPointEntity((DXFPointEntity&)*pE,*pT);
1101                 break;
1102             case DXF_CIRCLE:
1103                 DrawCircleEntity((DXFCircleEntity&)*pE,*pT);
1104                 break;
1105             case DXF_ARC:
1106                 DrawArcEntity((DXFArcEntity&)*pE,*pT);
1107                 break;
1108             case DXF_TRACE:
1109                 DrawTraceEntity((DXFTraceEntity&)*pE,*pT);
1110                 break;
1111             case DXF_SOLID:
1112                 DrawSolidEntity((DXFSolidEntity&)*pE,*pT);
1113                 break;
1114             case DXF_TEXT:
1115                 if (b3DText)
1116                     Draw3DTextEntity((DXFTextEntity&)*pE,*pT);
1117                 else
1118                     DrawTextEntity((DXFTextEntity&)*pE,*pT);
1119                 break;
1120             case DXF_INSERT:
1121                 DrawInsertEntity((DXFInsertEntity&)*pE,*pT);
1122                 break;
1123             case DXF_ATTRIB:
1124                 DrawAttribEntity((DXFAttribEntity&)*pE,*pT);
1125                 break;
1126             case DXF_POLYLINE:
1127                 DrawPolyLineEntity((DXFPolyLineEntity&)*pE,*pT);
1128                 break;
1129             case DXF_LWPOLYLINE :
1130                 DrawLWPolyLineEntity((DXFLWPolyLineEntity&)*pE, *pT);
1131                 break;
1132             case DXF_HATCH :
1133                 DrawHatchEntity((DXFHatchEntity&)*pE, *pT);
1134                 break;
1135             case DXF_ELLIPSE:
1136                 DrawEllipseEntity((DXFEllipseEntity&)*pE,*pT);
1137                 break;
1138             case DXF_SPLINE:
1139                 DrawSplineEntity((DXFSplineEntity&)*pE,*pT);
1140                 break;
1141             case DXF_3DFACE:
1142                 Draw3DFaceEntity((DXF3DFaceEntity&)*pE,*pT);
1143                 break;
1144             case DXF_DIMENSION:
1145                 DrawDimensionEntity((DXFDimensionEntity&)*pE,*pT);
1146                 break;
1147             default:
1148                 break;  // four other values not handled -Wall
1149             }
1150         }
1151         pE=pE->pSucc;
1152         nCount++;
1153         if (bTopEntities) MayCallback(nCount);
1154     }
1155 }
1156 
1157 
DXF2GDIMetaFile()1158 DXF2GDIMetaFile::DXF2GDIMetaFile()
1159 {
1160 }
1161 
1162 
~DXF2GDIMetaFile()1163 DXF2GDIMetaFile::~DXF2GDIMetaFile()
1164 {
1165 }
1166 
1167 
Convert(const DXFRepresentation & rDXF,GDIMetaFile & rMTF,sal_uInt16 nminpercent,sal_uInt16 nmaxpercent)1168 sal_Bool DXF2GDIMetaFile::Convert(const DXFRepresentation & rDXF, GDIMetaFile & rMTF, sal_uInt16 nminpercent, sal_uInt16 nmaxpercent)
1169 {
1170     double fWidth,fHeight,fScale;
1171     DXFTransform aTransform;
1172     Size aPrefSize;
1173     const DXFLayer * pLayer;
1174     const DXFVPort * pVPort;
1175 
1176     pVirDev = new VirtualDevice;
1177     pDXF    = &rDXF;
1178     bStatus = sal_True;
1179 
1180     OptPointsPerCircle=50;
1181 
1182     nMinPercent=(sal_uLong)nminpercent;
1183     nMaxPercent=(sal_uLong)nmaxpercent;
1184     nLastPercent=nMinPercent;
1185     nMainEntitiesCount=CountEntities(pDXF->aEntities);
1186 
1187     nBlockColor=7;
1188     aBlockDXFLineInfo.eStyle = LINE_SOLID;
1189     aBlockDXFLineInfo.fWidth = 0;
1190     aBlockDXFLineInfo.nDashCount = 0;
1191     aBlockDXFLineInfo.fDashLen = 0;
1192     aBlockDXFLineInfo.nDotCount = 0;
1193     aBlockDXFLineInfo.fDotLen = 0;
1194     aBlockDXFLineInfo.fDistance = 0;
1195 
1196     pLayer=pDXF->aTables.SearchLayer("0");
1197     if (pLayer!=NULL) {
1198         nParentLayerColor=pLayer->nColor & 0xff;
1199         aParentLayerDXFLineInfo=LTypeToDXFLineInfo(pLayer->sLineType);
1200     }
1201     else {
1202         nParentLayerColor=7;
1203         aParentLayerDXFLineInfo.eStyle = LINE_SOLID;
1204         aParentLayerDXFLineInfo.fWidth = 0;
1205         aParentLayerDXFLineInfo.nDashCount = 0;
1206         aParentLayerDXFLineInfo.fDashLen = 0;
1207         aParentLayerDXFLineInfo.nDotCount = 0;
1208         aParentLayerDXFLineInfo.fDotLen = 0;
1209         aParentLayerDXFLineInfo.fDistance = 0;
1210     }
1211 
1212     pVirDev->EnableOutput(sal_False);
1213     rMTF.Record(pVirDev);
1214 
1215     aActLineColor = pVirDev->GetLineColor();
1216     aActFillColor = pVirDev->GetFillColor();
1217     aActFont = pVirDev->GetFont();
1218 
1219     pVPort=pDXF->aTables.SearchVPort("*ACTIVE");
1220     if (pVPort!=NULL) {
1221         if (pVPort->aDirection.fx==0 && pVPort->aDirection.fy==0)
1222             pVPort=NULL;
1223     }
1224     // A non-top viewport means the drawing is projected in 3D; text then has to be
1225     // laid into that projection (Draw3DTextEntity) rather than drawn flat.
1226     b3DText = (pVPort!=NULL) ? sal_True : sal_False;
1227 
1228     if (pVPort==NULL) {
1229         if (pDXF->aBoundingBox.bEmpty==sal_True)
1230             bStatus=sal_False;
1231         else {
1232             fWidth=pDXF->aBoundingBox.fMaxX-pDXF->aBoundingBox.fMinX;
1233             fHeight=pDXF->aBoundingBox.fMaxY-pDXF->aBoundingBox.fMinY;
1234             if (fWidth<=0 || fHeight<=0) {
1235                 bStatus=sal_False;
1236                 fScale = 0;  // -Wall added this...
1237             }
1238             else {
1239                 // Leave a small margin around the drawing. The larger extent is
1240                 // scaled to fill the 10000-unit target exactly, so geometry that
1241                 // lies right on the extent (e.g. a drawing frame's outer line)
1242                 // would otherwise sit precisely on the fit boundary and be
1243                 // clipped. This surfaced once issue 58347 made the bounding box
1244                 // a tight fit to the geometry; the margin keeps every edge inside
1245                 // the fitted area (symmetric, so all four sides get breathing room).
1246                 const double fMargin =
1247                     (fWidth>fHeight ? fWidth : fHeight) * 0.01;
1248                 const double fFitWidth  = fWidth  + 2.0*fMargin;
1249                 const double fFitHeight = fHeight + 2.0*fMargin;
1250 //              if (fWidth<500.0 || fHeight<500.0 || fWidth>32767.0 || fHeight>32767.0) {
1251                     if (fFitWidth>fFitHeight)
1252                         fScale=10000.0/fFitWidth;
1253                     else
1254                         fScale=10000.0/fFitHeight;
1255 //              }
1256 //              else
1257 //                  fScale=1.0;
1258                 aTransform=DXFTransform(fScale,-fScale,fScale,
1259                                         DXFVector((fMargin-pDXF->aBoundingBox.fMinX)*fScale,
1260                                                   (pDXF->aBoundingBox.fMaxY+fMargin)*fScale,
1261                                                   -pDXF->aBoundingBox.fMinZ*fScale));
1262                 aPrefSize.Width() =(long)(fFitWidth*fScale+1.5);
1263                 aPrefSize.Height()=(long)(fFitHeight*fScale+1.5);
1264             }
1265         }
1266     }
1267     else {
1268         fHeight=pVPort->fHeight;
1269         fWidth=fHeight*pVPort->fAspectRatio;
1270 //      if (fWidth<500.0 || fHeight<500.0 || fWidth>32767.0 || fHeight>32767.0) {
1271             if (fWidth>fHeight)
1272                 fScale=10000.0/fWidth;
1273             else
1274                 fScale=10000.0/fHeight;
1275 //      }
1276 //      else
1277 //          fScale=1.0;
1278         aTransform=DXFTransform(
1279             DXFTransform(pVPort->aDirection,pVPort->aTarget),
1280             DXFTransform(
1281                 DXFTransform(1.0,-1.0,1.0,DXFVector(fWidth/2-pVPort->fCenterX,fHeight/2+pVPort->fCenterY,0)),
1282                 DXFTransform(fScale,fScale,fScale,DXFVector(0,0,0))
1283             )
1284         );
1285         aPrefSize.Width() =(long)(fWidth*fScale+1.5);
1286         aPrefSize.Height()=(long)(fHeight*fScale+1.5);
1287     }
1288 
1289     if (bStatus==sal_True)
1290         DrawEntities(pDXF->aEntities,aTransform,sal_True);
1291 
1292     rMTF.Stop();
1293 
1294     if ( bStatus==sal_True )
1295     {
1296         rMTF.SetPrefSize( aPrefSize );
1297 
1298         // MapMode einfach, falls Grafik dann nicht zu klein wird (<0,5cm),
1299         // auf 1/100-mm (1/10-mm) setzen
1300         if( ( aPrefSize.Width() < 500 ) && ( aPrefSize.Height() < 500 ) )
1301             rMTF.SetPrefMapMode( MapMode( MAP_10TH_MM ) );
1302         else
1303             rMTF.SetPrefMapMode( MapMode( MAP_100TH_MM ) );
1304     }
1305 
1306     delete pVirDev;
1307     return bStatus;
1308 }
1309