diff options
23 files changed, 799 insertions, 256 deletions
diff --git a/drawinglayer/source/processor2d/canvasprocessor.cxx b/drawinglayer/source/processor2d/canvasprocessor.cxx index 4fd65b543d..77cb2b3350 100644 --- a/drawinglayer/source/processor2d/canvasprocessor.cxx +++ b/drawinglayer/source/processor2d/canvasprocessor.cxx @@ -2106,7 +2106,10 @@ namespace drawinglayer mpOutputDevice->Push(PUSH_MAPMODE); mpOutputDevice->SetMapMode(maOriginalMapMode); - if(!renderChartPrimitive2D(rChartPrimitive, *mpOutputDevice)) + if(!renderChartPrimitive2D( + rChartPrimitive, + *mpOutputDevice, + getViewInformation2D())) { // fallback to decomposition (MetaFile) process(rChartPrimitive.get2DDecomposition(getViewInformation2D())); diff --git a/drawinglayer/source/processor2d/helperchartrenderer.cxx b/drawinglayer/source/processor2d/helperchartrenderer.cxx index 3d1f6a1afe..383f678e28 100644 --- a/drawinglayer/source/processor2d/helperchartrenderer.cxx +++ b/drawinglayer/source/processor2d/helperchartrenderer.cxx @@ -41,6 +41,7 @@ #include <svtools/chartprettypainter.hxx> #include <com/sun/star/lang/XUnoTunnel.hpp> #include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <drawinglayer/geometry/viewinformation2d.hxx> ////////////////////////////////////////////////////////////////////////////// @@ -50,7 +51,10 @@ using namespace com::sun::star; namespace drawinglayer { - bool renderChartPrimitive2D(const primitive2d::ChartPrimitive2D& rChartCandidate, OutputDevice& rOutputDevice) + bool renderChartPrimitive2D( + const primitive2d::ChartPrimitive2D& rChartCandidate, + OutputDevice& rOutputDevice, + const geometry::ViewInformation2D& rViewInformation2D) { bool bChartRendered(false); @@ -73,14 +77,74 @@ namespace drawinglayer if( pPrettyPainter ) { - // create logic object range + // create logic object range; do NOT use ObjectTransformation for this + // (rViewInformation2D.getObjectTransformation()), only the logic object + // size is wanted basegfx::B2DRange aObjectRange(0.0, 0.0, 1.0, 1.0); aObjectRange.transform(rChartCandidate.getTransformation()); const Rectangle aRectangle( (sal_Int32)aObjectRange.getMinX(), (sal_Int32)aObjectRange.getMinY(), (sal_Int32)aObjectRange.getMaxX(), (sal_Int32)aObjectRange.getMaxY()); - bChartRendered = pPrettyPainter->DoPaint(&rOutputDevice, aRectangle); + // #i101811# + if(rViewInformation2D.getObjectTransformation().isIdentity()) + { + // no embedding in another transfromation, just paint with existing + // MapMode. This is just a shortcut; using the below code will also + // work; it has just a neutral ObjectTransformation + bChartRendered = pPrettyPainter->DoPaint(&rOutputDevice, aRectangle); + } + else + { + // rViewInformation2D.getObjectTransformation() is used and + // needs to be expressed in the MapMode for the PrettyPainter; + // else it would call ChartModelHelper::setPageSize(...) with the + // changed size what really will change the chart model and leads + // to re-layouts and re-formattings + const MapMode aOldMapMode(rOutputDevice.GetMapMode()); + basegfx::B2DVector aVTScale, aScale, aTranslate; + double fRotate, fShearX; + + // get basic scaling with current MapMode (aVTScale), containing + // mapping for set MapUnit (e.g. for 100th mm, the basic scale is + // not 1.0, 1.0). This is needed since this scale is included in + // the ObjectToView Transformation and needs to be removed (see + // correction below) to re-create a MapMode + rOutputDevice.SetMapMode(aOldMapMode.GetMapUnit()); + rOutputDevice.GetViewTransformation().decompose(aVTScale, aTranslate, fRotate, fShearX); + + // get complete ObjectToView Transformation scale and translate from current + // transformation chain (combined view and object transform) + rViewInformation2D.getObjectToViewTransformation().decompose(aScale, aTranslate, fRotate, fShearX); + + // assert when shear and/or rotation is used + OSL_ENSURE(basegfx::fTools::equalZero(fRotate), "Chart PrettyPrinting with unsupportable rotation (!)"); + OSL_ENSURE(basegfx::fTools::equalZero(fShearX), "Chart PrettyPrinting with unsupportable shear (!)"); + + // clean scale and translate from basic scaling (DPI, etc...) + // since this will implicitely be part of the to-be-created MapMode + const basegfx::B2DTuple aBasicCleaner( + basegfx::fTools::equalZero(aVTScale.getX()) ? 1.0 : 1.0 / aVTScale.getX(), + basegfx::fTools::equalZero(aVTScale.getY()) ? 1.0 : 1.0 / aVTScale.getY()); + aScale *= aBasicCleaner; + aTranslate *= aBasicCleaner; + + // for MapMode, take scale out of translation + const basegfx::B2DTuple aScaleRemover( + basegfx::fTools::equalZero(aScale.getX()) ? 1.0 : 1.0 / aScale.getX(), + basegfx::fTools::equalZero(aScale.getY()) ? 1.0 : 1.0 / aScale.getY()); + aTranslate *= aScaleRemover; + + // build new MapMode + const MapMode aNewMapMode(aOldMapMode.GetMapUnit(), + Point(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY())), + Fraction(aScale.getX()), Fraction(aScale.getY())); + + // use, paint, restore + rOutputDevice.SetMapMode(aNewMapMode); + bChartRendered = pPrettyPainter->DoPaint(&rOutputDevice, aRectangle); + rOutputDevice.SetMapMode(aOldMapMode); + } } } } diff --git a/drawinglayer/source/processor2d/helperchartrenderer.hxx b/drawinglayer/source/processor2d/helperchartrenderer.hxx index de13431c79..e670cccdf2 100644 --- a/drawinglayer/source/processor2d/helperchartrenderer.hxx +++ b/drawinglayer/source/processor2d/helperchartrenderer.hxx @@ -43,16 +43,21 @@ class OutputDevice; -namespace drawinglayer { namespace primitive2d { - class ChartPrimitive2D; -}} +namespace drawinglayer { namespace primitive2d { class ChartPrimitive2D; }} +namespace drawinglayer { namespace geometry { class ViewInformation2D; }} ////////////////////////////////////////////////////////////////////////////// // support chart PrettyPrinter usage from primitives namespace drawinglayer { - bool renderChartPrimitive2D(const primitive2d::ChartPrimitive2D& rChartCandidate, OutputDevice& rOutputDevice); + // #i101811# + // Added current ViewInformation2D to take evtl. changed + // ObjectTransformation into account + bool renderChartPrimitive2D( + const primitive2d::ChartPrimitive2D& rChartCandidate, + OutputDevice& rOutputDevice, + const geometry::ViewInformation2D& rViewInformation2D); } // end of namespace drawinglayer diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx index f94001266f..c75e5003cc 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -247,8 +247,8 @@ namespace drawinglayer if(rB2DPolygon.count() && !mnSvtGraphicStrokeCount) { basegfx::BColor aStrokeColor; - PolyPolygon aStartPolyPolygon; - PolyPolygon aEndPolyPolygon; + basegfx::B2DPolyPolygon aStartArrow; + basegfx::B2DPolyPolygon aEndArrow; if(pColor) { @@ -271,11 +271,9 @@ namespace drawinglayer { fPolyLength = basegfx::tools::getLength(rB2DPolygon); - const basegfx::B2DPolyPolygon aStartArrow(basegfx::tools::createAreaGeometryForLineStartEnd( + aStartArrow = basegfx::tools::createAreaGeometryForLineStartEnd( rB2DPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(), - fPolyLength, pStart->isCentered() ? 0.5 : 0.0, 0)); - - aStartPolyPolygon = PolyPolygon(aStartArrow); + fPolyLength, pStart->isCentered() ? 0.5 : 0.0, 0); } if(pEnd && pEnd->isActive()) @@ -285,11 +283,9 @@ namespace drawinglayer fPolyLength = basegfx::tools::getLength(rB2DPolygon); } - const basegfx::B2DPolyPolygon aEndArrow(basegfx::tools::createAreaGeometryForLineStartEnd( + aEndArrow = basegfx::tools::createAreaGeometryForLineStartEnd( rB2DPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(), - fPolyLength, pEnd->isCentered() ? 0.5 : 0.0, 0)); - - aEndPolyPolygon = PolyPolygon(aEndArrow); + fPolyLength, pEnd->isCentered() ? 0.5 : 0.0, 0); } } @@ -341,10 +337,23 @@ namespace drawinglayer aDashArray = pStrokeAttribute->getDotDashArray(); } + // #i101734# apply current object transformation to created geometry. + // This is a partial fix. When a object transformation is used which + // e.g. contains a scaleX != scaleY, an unproportional scaling would + // have to be applied to the evtl. existing fat line. The current + // concept of PDF export and SvtGraphicStroke usage does simply not + // allow handling such definitions. The only clean way would be to + // add the transformation to SvtGraphicStroke and to handle it there + basegfx::B2DPolygon aB2DPolygon(rB2DPolygon); + + aB2DPolygon.transform(maCurrentTransformation); + aStartArrow.transform(maCurrentTransformation); + aEndArrow.transform(maCurrentTransformation); + pRetval = new SvtGraphicStroke( - Polygon(rB2DPolygon), - aStartPolyPolygon, - aEndPolyPolygon, + Polygon(aB2DPolygon), + PolyPolygon(aStartArrow), + PolyPolygon(aEndArrow), mfCurrentUnifiedTransparence, fLineWidth, SvtGraphicStroke::capButt, @@ -1001,7 +1010,68 @@ namespace drawinglayer adaptLineToFillDrawMode(); impStartSvtGraphicStroke(pSvtGraphicStroke); - process(rCandidate.get2DDecomposition(getViewInformation2D())); + + // #i101491# + // Change default of fat line generation for MetaFiles: Create MetaPolyLineAction + // instead of decomposing all geometries when the polygon has more than given amount of + // points; else the decomposition will get too expensive quiclky. OTOH + // the decomposition provides the better quality e.g. taking edge roundings + // into account which will NOT be taken into account with LineInfo-based actions + const sal_uInt32 nSubPolygonCount(rStrokePrimitive.getB2DPolygon().count()); + bool bDone(0 == nSubPolygonCount); + + if(!bDone && nSubPolygonCount > 1000) + { + // create MetaPolyLineActions, but without LINE_DASH + const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute(); + + if(basegfx::fTools::more(rLine.getWidth(), 0.0)) + { + const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute(); + basegfx::B2DPolyPolygon aHairLinePolyPolygon; + + if(0.0 == rStroke.getFullDotDashLen()) + { + aHairLinePolyPolygon.append(rStrokePrimitive.getB2DPolygon()); + } + else + { + basegfx::tools::applyLineDashing( + rStrokePrimitive.getB2DPolygon(), rStroke.getDotDashArray(), + &aHairLinePolyPolygon, 0, rStroke.getFullDotDashLen()); + } + + const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLine.getColor())); + mpOutputDevice->SetLineColor(Color(aHairlineColor)); + mpOutputDevice->SetFillColor(); + + aHairLinePolyPolygon.transform(maCurrentTransformation); + + const LineInfo aLineInfo(LINE_SOLID, basegfx::fround(rLine.getWidth())); + + for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++) + { + const basegfx::B2DPolygon aCandidate(aHairLinePolyPolygon.getB2DPolygon(a)); + + if(aCandidate.count() > 1) + { + const Polygon aToolsPolygon(aCandidate); + + mrMetaFile.AddAction(new MetaPolyLineAction(aToolsPolygon, aLineInfo)); + } + } + + bDone = true; + } + } + + if(!bDone) + { + // use decomposition (creates line geometry as filled polygon + // geometry) + process(rCandidate.get2DDecomposition(getViewInformation2D())); + } + impEndSvtGraphicStroke(pSvtGraphicStroke); // restore DrawMode @@ -1623,7 +1693,10 @@ namespace drawinglayer // ChartPrimitive2D const primitive2d::ChartPrimitive2D& rChartPrimitive = static_cast< const primitive2d::ChartPrimitive2D& >(rCandidate); - if(!renderChartPrimitive2D(rChartPrimitive, *mpOutputDevice)) + if(!renderChartPrimitive2D( + rChartPrimitive, + *mpOutputDevice, + getViewInformation2D())) { // fallback to decomposition (MetaFile) process(rChartPrimitive.get2DDecomposition(getViewInformation2D())); diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx index 7e7a909bec..73424a2e5e 100644 --- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx @@ -425,7 +425,10 @@ namespace drawinglayer mpOutputDevice->Push(PUSH_MAPMODE); mpOutputDevice->SetMapMode(maOriginalMapMode); - if(!renderChartPrimitive2D(rChartPrimitive, *mpOutputDevice)) + if(!renderChartPrimitive2D( + rChartPrimitive, + *mpOutputDevice, + getViewInformation2D())) { // fallback to decomposition (MetaFile) process(rChartPrimitive.get2DDecomposition(getViewInformation2D())); diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx index 8407968c2e..2e740f4412 100644 --- a/drawinglayer/source/processor2d/vclprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx @@ -1012,6 +1012,8 @@ namespace drawinglayer void VclProcessor2D::RenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate) { + // #i101491# method restructured to clearly use the DrawPolyLine + // calls starting from a deined line width const attribute::LineAttribute& rLineAttribute = rPolygonStrokeCandidate.getLineAttribute(); const double fLineWidth(rLineAttribute.getWidth()); bool bDone(false); @@ -1020,157 +1022,163 @@ namespace drawinglayer { const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(fLineWidth, 0.0)); const double fDiscreteLineWidth(aDiscreteUnit.getLength()); - const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing()); - const double fAllowedUpperBound(bAntiAliased ? 3.0 : 2.5); + const attribute::StrokeAttribute& rStrokeAttribute = rPolygonStrokeCandidate.getStrokeAttribute(); + const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor())); + basegfx::B2DPolyPolygon aHairlinePolyPolygon; - if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, fAllowedUpperBound)) + mpOutputDevice->SetLineColor(Color(aHairlineColor)); + mpOutputDevice->SetFillColor(); + + if(0.0 == rStrokeAttribute.getFullDotDashLen()) + { + // no line dashing, just copy + aHairlinePolyPolygon.append(rPolygonStrokeCandidate.getB2DPolygon()); + } + else { - // force to hairline - const attribute::StrokeAttribute& rStrokeAttribute = rPolygonStrokeCandidate.getStrokeAttribute(); - const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor())); - basegfx::B2DPolyPolygon aHairlinePolyPolygon; + // else apply LineStyle + basegfx::tools::applyLineDashing(rPolygonStrokeCandidate.getB2DPolygon(), + rStrokeAttribute.getDotDashArray(), + &aHairlinePolyPolygon, 0, rStrokeAttribute.getFullDotDashLen()); + } - mpOutputDevice->SetLineColor(Color(aHairlineColor)); - mpOutputDevice->SetFillColor(); + const sal_uInt32 nCount(aHairlinePolyPolygon.count()); - if(0.0 == rStrokeAttribute.getFullDotDashLen()) - { - // no line dashing, just copy - aHairlinePolyPolygon.append(rPolygonStrokeCandidate.getB2DPolygon()); - } - else - { - // else apply LineStyle - basegfx::tools::applyLineDashing(rPolygonStrokeCandidate.getB2DPolygon(), - rStrokeAttribute.getDotDashArray(), - &aHairlinePolyPolygon, 0, rStrokeAttribute.getFullDotDashLen()); - } - - const sal_uInt32 nCount(aHairlinePolyPolygon.count()); + if(nCount) + { + const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing()); + aHairlinePolyPolygon.transform(maCurrentTransformation); - if(nCount) + for(sal_uInt32 a(0); a < nCount; a++) { - aHairlinePolyPolygon.transform(maCurrentTransformation); + basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a)); if(bAntiAliased) { - for(sal_uInt32 a(0); a < nCount; a++) + if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.0)) + { + // line in range ]0.0 .. 1.0[ + // paint as simple hairline + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + bDone = true; + } + else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.0)) { - basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a)); + // line in range [1.0 .. 2.0[ + // paint as 2x2 with dynamic line distance + basegfx::B2DHomMatrix aMat; + const double fDistance(fDiscreteLineWidth - 1.0); + const double fHalfDistance(fDistance * 0.5); + + aMat.set(0, 2, -fHalfDistance); + aMat.set(1, 2, -fHalfDistance); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, fDistance); + aMat.set(1, 2, 0.0); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, 0.0); + aMat.set(1, 2, fDistance); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, -fDistance); + aMat.set(1, 2, 0.0); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + bDone = true; + } + else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 3.0)) + { + // line in range [2.0 .. 3.0] + // paint as cross in a 3x3 with dynamic line distance + basegfx::B2DHomMatrix aMat; + const double fDistance((fDiscreteLineWidth - 1.0) * 0.5); - if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.0)) - { - // line in range ]0.0 .. 1.0[ - // paint as simple hairline - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - } - else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.0)) - { - // line in range [1.0 .. 2.0[ - // paint as 2x2 with dynamic line distance - basegfx::B2DHomMatrix aMat; - const double fDistance(fDiscreteLineWidth - 1.0); - const double fHalfDistance(fDistance * 0.5); - - aMat.set(0, 2, -fHalfDistance); - aMat.set(1, 2, -fHalfDistance); - aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - - aMat.set(0, 2, fDistance); - aMat.set(1, 2, 0.0); - aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - - aMat.set(0, 2, 0.0); - aMat.set(1, 2, fDistance); - aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - - aMat.set(0, 2, -fDistance); - aMat.set(1, 2, 0.0); - aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - } - else - { - // line in range [2.0 .. 3.0] - // paint as cross in a 3x3 with dynamic line distance - basegfx::B2DHomMatrix aMat; - const double fDistance((fDiscreteLineWidth - 1.0) * 0.5); - - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - - aMat.set(0, 2, -fDistance); - aMat.set(1, 2, 0.0); - aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - - aMat.set(0, 2, fDistance); - aMat.set(1, 2, -fDistance); - aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - aMat.set(0, 2, fDistance); - aMat.set(1, 2, fDistance); - aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - - aMat.set(0, 2, -fDistance); - aMat.set(1, 2, fDistance); - aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - } + aMat.set(0, 2, -fDistance); + aMat.set(1, 2, 0.0); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, fDistance); + aMat.set(1, 2, -fDistance); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, fDistance); + aMat.set(1, 2, fDistance); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + + aMat.set(0, 2, -fDistance); + aMat.set(1, 2, fDistance); + aCandidate.transform(aMat); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + bDone = true; + } + else + { + // #i101491# line width above 3.0 } } else { - if(basegfx::fTools::more(fDiscreteLineWidth, 1.5)) + if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.5)) + { + // line width below 1.5, draw the basic hairline polygon + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + bDone = true; + } + else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.5)) { // line width is in range ]1.5 .. 2.5], use four hairlines // drawn in a square basegfx::B2DHomMatrix aMat; + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - for(sal_uInt32 a(0); a < nCount; a++) - { - basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a)); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - - aMat.set(0, 2, 1.0); - aMat.set(1, 2, 0.0); - aCandidate.transform(aMat); + aMat.set(0, 2, 1.0); + aMat.set(1, 2, 0.0); + aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - aMat.set(0, 2, 0.0); - aMat.set(1, 2, 1.0); - aCandidate.transform(aMat); + aMat.set(0, 2, 0.0); + aMat.set(1, 2, 1.0); + aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - aMat.set(0, 2, -1.0); - aMat.set(1, 2, 0.0); - aCandidate.transform(aMat); + aMat.set(0, 2, -1.0); + aMat.set(1, 2, 0.0); + aCandidate.transform(aMat); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - } + mpOutputDevice->DrawPolyLine(aCandidate, 0.0); + bDone = true; } else { - for(sal_uInt32 a(0); a < nCount; a++) - { - // draw the basic hairline polygon - const basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a)); - mpOutputDevice->DrawPolyLine(aCandidate, 0.0); - } + // #i101491# line width is above 2.5 } } - } - bDone = true; + if(!bDone && rPolygonStrokeCandidate.getB2DPolygon().count() > 1000) + { + // #i101491# If the polygon complexity uses more than a given amount, do + // use OuputDevice::DrawPolyLine directly; this will avoid buffering all + // decompositions in primtives (memory) and fallback to old line painting + // for very complex polygons, too + mpOutputDevice->DrawPolyLine(aCandidate, fDiscreteLineWidth, rLineAttribute.getLineJoin()); + bDone = true; + } + } } } - + if(!bDone) { // remeber that we enter a PolygonStrokePrimitive2D decomposition, diff --git a/svx/inc/svx/sdr/attribute/sdrtextattribute.hxx b/svx/inc/svx/sdr/attribute/sdrtextattribute.hxx index 6193488113..8046708d0a 100644 --- a/svx/inc/svx/sdr/attribute/sdrtextattribute.hxx +++ b/svx/inc/svx/sdr/attribute/sdrtextattribute.hxx @@ -62,6 +62,9 @@ namespace drawinglayer sal_Int32 maTextRightDistance; sal_Int32 maTextLowerDistance; + // #i101556# use versioning from text attributes to detect changes + sal_uInt32 maPropertiesVersion; + // bitfield unsigned mbContour : 1; unsigned mbFitToSize : 1; @@ -102,6 +105,7 @@ namespace drawinglayer sal_Int32 getTextUpperDistance() const { return maTextUpperDistance; } sal_Int32 getTextRightDistance() const { return maTextRightDistance; } sal_Int32 getTextLowerDistance() const { return maTextLowerDistance; } + sal_uInt32 getPropertiesVersion() const { return maPropertiesVersion; } // animation timing generation void getBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const; diff --git a/svx/inc/svx/sdr/contact/viewcontactofgraphic.hxx b/svx/inc/svx/sdr/contact/viewcontactofgraphic.hxx index f977de554d..322b55b69a 100644 --- a/svx/inc/svx/sdr/contact/viewcontactofgraphic.hxx +++ b/svx/inc/svx/sdr/contact/viewcontactofgraphic.hxx @@ -38,6 +38,8 @@ // predeclarations class SdrGrafObj; +namespace drawinglayer { namespace attribute { class SdrLineFillShadowTextAttribute; }} +class GraphicAttr; ////////////////////////////////////////////////////////////////////////////// @@ -47,6 +49,16 @@ namespace sdr { class ViewContactOfGraphic : public ViewContactOfTextObj { + private: + // helpers for constructing various primitive visualisations in various states + drawinglayer::primitive2d::Primitive2DSequence createVIP2DSForPresObj( + const basegfx::B2DHomMatrix& rObjectMatrix, + const drawinglayer::attribute::SdrLineFillShadowTextAttribute& rAttribute, + const GraphicAttr& rLocalGrafInfo) const; + drawinglayer::primitive2d::Primitive2DSequence createVIP2DSForDraft( + const basegfx::B2DHomMatrix& rObjectMatrix, + const drawinglayer::attribute::SdrLineFillShadowTextAttribute& rAttribute) const; + protected: // Create a Object-Specific ViewObjectContact, set ViewContact and // ObjectContact. Always needs to return something. @@ -63,6 +75,13 @@ namespace sdr ViewContactOfGraphic(SdrGrafObj& rGrafObj); virtual ~ViewContactOfGraphic(); + // #i102380# + void flushGraphicObjects(); + + // helpers for viusualisation state + bool visualisationUsesPresObj() const; + bool visualisationUsesDraft() const; + protected: // This method is responsible for creating the graphical visualisation data // ONLY based on model data diff --git a/svx/inc/svx/sdr/primitive2d/sdrpathprimitive2d.hxx b/svx/inc/svx/sdr/primitive2d/sdrpathprimitive2d.hxx index defaa73eff..199f3c4fdc 100644 --- a/svx/inc/svx/sdr/primitive2d/sdrpathprimitive2d.hxx +++ b/svx/inc/svx/sdr/primitive2d/sdrpathprimitive2d.hxx @@ -49,9 +49,9 @@ namespace drawinglayer class SdrPathPrimitive2D : public BasePrimitive2D { private: - ::basegfx::B2DHomMatrix maTransform; + basegfx::B2DHomMatrix maTransform; attribute::SdrLineFillShadowTextAttribute maSdrLFSTAttribute; - ::basegfx::B2DPolyPolygon maUnitPolyPolygon; + basegfx::B2DPolyPolygon maUnitPolyPolygon; protected: // local decomposition. @@ -59,14 +59,14 @@ namespace drawinglayer public: SdrPathPrimitive2D( - const ::basegfx::B2DHomMatrix& rTransform, + const basegfx::B2DHomMatrix& rTransform, const attribute::SdrLineFillShadowTextAttribute& rSdrLFSTAttribute, - const ::basegfx::B2DPolyPolygon& rUnitPolyPolygon); + const basegfx::B2DPolyPolygon& rUnitPolyPolygon); // data access - const ::basegfx::B2DHomMatrix& getTransform() const { return maTransform; } + const basegfx::B2DHomMatrix& getTransform() const { return maTransform; } const attribute::SdrLineFillShadowTextAttribute& getSdrLFSTAttribute() const { return maSdrLFSTAttribute; } - const ::basegfx::B2DPolyPolygon& getUnitPolyPolygon() const { return maUnitPolyPolygon; } + const basegfx::B2DPolyPolygon& getUnitPolyPolygon() const { return maUnitPolyPolygon; } // compare operator virtual bool operator==(const BasePrimitive2D& rPrimitive) const; diff --git a/svx/inc/svx/sdr/properties/properties.hxx b/svx/inc/svx/sdr/properties/properties.hxx index 855b8a2504..2b8928e376 100644 --- a/svx/inc/svx/sdr/properties/properties.hxx +++ b/svx/inc/svx/sdr/properties/properties.hxx @@ -184,6 +184,11 @@ namespace sdr // ClearItemAndBroadcast() and SetItemSetAndBroadcast(), see above. // But also from inside SdrObjects. void BroadcastItemChange(const ItemChangeBroadcaster& rChange); + + // #i101556# add versioning mechanism; used from e.g. text attribute set to + // allow detection of e.g. style sheet or single text attribute changes. The + // default implementation returns 0 (zero) + virtual sal_uInt32 getVersion() const; }; } // end of namespace properties } // end of namespace sdr diff --git a/svx/inc/svx/sdr/properties/textproperties.hxx b/svx/inc/svx/sdr/properties/textproperties.hxx index 5dcc8f5d3f..08f2603a76 100644 --- a/svx/inc/svx/sdr/properties/textproperties.hxx +++ b/svx/inc/svx/sdr/properties/textproperties.hxx @@ -42,6 +42,10 @@ namespace sdr { class SVX_DLLPUBLIC TextProperties : public AttributeProperties { + private: + // #i101556# versioning support + sal_uInt32 maVersion; + protected: // create a new itemset virtual SfxItemSet& CreateObjectSpecificItemSet(SfxItemPool& rPool); @@ -82,6 +86,9 @@ namespace sdr // Set single item at the local ItemSet. *Does not use* AllowItemChange(), // ItemChange(), PostItemChange() and ItemSetChanged() calls. void SetObjectItemNoBroadcast(const SfxPoolItem& rItem); + + // #i101556# versioning support + virtual sal_uInt32 getVersion() const; }; } // end of namespace properties } // end of namespace sdr diff --git a/svx/inc/svx/svdograf.hxx b/svx/inc/svx/svdograf.hxx index 8c7b47c0cb..f54281822e 100644 --- a/svx/inc/svx/svdograf.hxx +++ b/svx/inc/svx/svdograf.hxx @@ -227,6 +227,9 @@ public: sal_Bool IsObjectTransparent() const; ::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream > getInputStream(); + + // #i103116# FullDrag support + virtual SdrObject* getFullDragClone() const; }; #endif //_SVDOGRAF_HXX diff --git a/svx/source/outliner/outliner.cxx b/svx/source/outliner/outliner.cxx index 7a298c4c4b..f46dedff1c 100644 --- a/svx/source/outliner/outliner.cxx +++ b/svx/source/outliner/outliner.cxx @@ -1526,7 +1526,7 @@ void Outliner::ImplCheckParagraphs( USHORT nStart, USHORT nEnd ) if (pPara) { pPara->Invalidate(); - ImplCalcBulletText( n, FALSE, FALSE ); + ImplCalcBulletText( (USHORT)n, FALSE, FALSE ); } } } diff --git a/svx/source/sdr/attribute/sdrtextattribute.cxx b/svx/source/sdr/attribute/sdrtextattribute.cxx index 591fcb026a..020ec57ef3 100644 --- a/svx/source/sdr/attribute/sdrtextattribute.cxx +++ b/svx/source/sdr/attribute/sdrtextattribute.cxx @@ -35,6 +35,7 @@ #include <svx/outlobj.hxx> #include <svx/editobj.hxx> #include <svx/flditem.hxx> +#include <svx/sdr/properties/properties.hxx> ////////////////////////////////////////////////////////////////////////////// @@ -63,6 +64,7 @@ namespace drawinglayer maTextUpperDistance(aTextUpperDistance), maTextRightDistance(aTextRightDistance), maTextLowerDistance(aTextLowerDistance), + maPropertiesVersion(0), mbContour(bContour), mbFitToSize(bFitToSize), mbHideContour(bHideContour), @@ -70,6 +72,10 @@ namespace drawinglayer mbScroll(bScroll), mbInEditMode(bInEditMode) { + // #i101556# init with version number to detect changes of single text + // attribute and/or style sheets in primitive data without having to + // copy that data locally (which would be better from principle) + maPropertiesVersion = rSdrText.GetObject().GetProperties().getVersion(); } bool SdrTextAttribute::operator==(const SdrTextAttribute& rCandidate) const @@ -80,6 +86,7 @@ namespace drawinglayer && getTextUpperDistance() == rCandidate.getTextUpperDistance() && getTextRightDistance() == rCandidate.getTextRightDistance() && getTextLowerDistance() == rCandidate.getTextLowerDistance() + && getPropertiesVersion() == rCandidate.getPropertiesVersion() && isContour() == rCandidate.isContour() && isFitToSize() == rCandidate.isFitToSize() && isHideContour() == rCandidate.isHideContour() diff --git a/svx/source/sdr/contact/viewcontactofgraphic.cxx b/svx/source/sdr/contact/viewcontactofgraphic.cxx index 3f6ac13caf..40f005dd5c 100644 --- a/svx/source/sdr/contact/viewcontactofgraphic.cxx +++ b/svx/source/sdr/contact/viewcontactofgraphic.cxx @@ -53,6 +53,18 @@ #include <svdglob.hxx> #include <vcl/svapp.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> + +#include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx> +#include <svx/eeitem.hxx> +#include <svx/colritem.hxx> +//#include <svx/xtable.hxx> + ////////////////////////////////////////////////////////////////////////////// namespace sdr @@ -78,6 +90,238 @@ namespace sdr { } + void ViewContactOfGraphic::flushGraphicObjects() + { + // #i102380# The graphic is swapped out. To let that have an effect ist is necessary to + // delete copies of the GraphicObject which are not swapped out and have no SwapHandler set + // (this is what happnes when the GraphicObject gets copied to a SdrGrafPrimitive2D). This + // is best achieved for the VC by clearing the local decomposition cache. It would be possible + // to also do this for the VOC cache, but that VOCs exist exactly expresss that the object + // gets visualised, so this would be wrong. + flushViewIndependentPrimitive2DSequence(); + } + + drawinglayer::primitive2d::Primitive2DSequence ViewContactOfGraphic::createVIP2DSForPresObj( + const basegfx::B2DHomMatrix& rObjectMatrix, + const drawinglayer::attribute::SdrLineFillShadowTextAttribute& rAttribute, + const GraphicAttr& rLocalGrafInfo) const + { + drawinglayer::primitive2d::Primitive2DSequence xRetval; + GraphicObject aEmptyGraphicObject; + GraphicAttr aEmptyGraphicAttr; + + // SdrGrafPrimitive2D without content in original size which carries all eventual attributes and texts + const drawinglayer::primitive2d::Primitive2DReference xReferenceA(new drawinglayer::primitive2d::SdrGrafPrimitive2D( + rObjectMatrix, + rAttribute, + aEmptyGraphicObject, + aEmptyGraphicAttr)); + xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReferenceA, 1); + + // SdrGrafPrimitive2D with content (which is the preview graphic) scaled to smaller size and + // without attributes + basegfx::B2DHomMatrix aSmallerMatrix; + + // #i94431# for some reason, i forgot to take the PrefMapMode of the graphic + // into account. Since EmptyPresObj's are only used in Draw/Impress, it is + // safe to assume 100th mm as target. + Size aPrefSize(GetGrafObject().GetGrafPrefSize()); + + if(MAP_PIXEL == GetGrafObject().GetGrafPrefMapMode().GetMapUnit()) + { + aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MAP_100TH_MM); + } + else + { + aPrefSize = Application::GetDefaultDevice()->LogicToLogic(aPrefSize, GetGrafObject().GetGrafPrefMapMode(), MAP_100TH_MM); + } + + // decompose object matrix to get single values + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + + const double fOffsetX((aScale.getX() - aPrefSize.getWidth()) / 2.0); + const double fOffsetY((aScale.getY() - aPrefSize.getHeight()) / 2.0); + + if(basegfx::fTools::moreOrEqual(fOffsetX, 0.0) && basegfx::fTools::moreOrEqual(fOffsetY, 0.0)) + { + // create the EmptyPresObj fallback visualisation. The fallback graphic + // is already provided in rGraphicObject in this case, use it + aSmallerMatrix.scale(aPrefSize.getWidth(), aPrefSize.getHeight()); + aSmallerMatrix.translate(fOffsetX, fOffsetY); + aSmallerMatrix.shearX(fShearX); + aSmallerMatrix.rotate(fRotate); + aSmallerMatrix.translate(aTranslate.getX(), aTranslate.getY()); + + const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject(false); + const drawinglayer::attribute::SdrLineFillShadowTextAttribute aEmptyAttributes(0, 0, 0, 0, 0, 0); + const drawinglayer::primitive2d::Primitive2DReference xReferenceB(new drawinglayer::primitive2d::SdrGrafPrimitive2D( + aSmallerMatrix, + aEmptyAttributes, + rGraphicObject, + rLocalGrafInfo)); + + drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(xRetval, xReferenceB); + } + + return xRetval; + } + + drawinglayer::primitive2d::Primitive2DSequence ViewContactOfGraphic::createVIP2DSForDraft( + const basegfx::B2DHomMatrix& rObjectMatrix, + const drawinglayer::attribute::SdrLineFillShadowTextAttribute& rAttribute) const + { + drawinglayer::primitive2d::Primitive2DSequence xRetval; + GraphicObject aEmptyGraphicObject; + GraphicAttr aEmptyGraphicAttr; + + // SdrGrafPrimitive2D without content in original size which carries all eventual attributes and texts + const drawinglayer::primitive2d::Primitive2DReference xReferenceA(new drawinglayer::primitive2d::SdrGrafPrimitive2D( + rObjectMatrix, + rAttribute, + aEmptyGraphicObject, + aEmptyGraphicAttr)); + xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReferenceA, 1); + + if(!rAttribute.getLine()) + { + // create a surrounding frame when no linestyle given + const Color aColor(Application::GetSettings().GetStyleSettings().GetShadowColor()); + const basegfx::BColor aBColor(aColor.getBColor()); + const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); + + basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aUnitRange)); + aOutline.transform(rObjectMatrix); + + drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(xRetval, + drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( + aOutline, + aBColor))); + } + + // decompose object matrix to get single values + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + + // define a distance value, used for distance from bitmap to borders and from bitmap + // to text, too (2 mm) + const double fDistance(200.0); + + // consume borders from values + aScale.setX(std::max(0.0, aScale.getX() - (2.0 * fDistance))); + aScale.setY(std::max(0.0, aScale.getY() - (2.0 * fDistance))); + aTranslate.setX(aTranslate.getX() + fDistance); + aTranslate.setY(aTranslate.getY() + fDistance); + + // draw a draft bitmap + const Bitmap aDraftBitmap(ResId(BMAP_GrafikEi, *ImpGetResMgr())); + + if(!aDraftBitmap.IsEmpty()) + { + Size aPrefSize(aDraftBitmap.GetPrefSize()); + + if(MAP_PIXEL == aDraftBitmap.GetPrefMapMode().GetMapUnit()) + { + aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aDraftBitmap.GetSizePixel(), MAP_100TH_MM); + } + else + { + aPrefSize = Application::GetDefaultDevice()->LogicToLogic(aPrefSize, aDraftBitmap.GetPrefMapMode(), MAP_100TH_MM); + } + + const double fBitmapScaling(2.0); + const double fWidth(aPrefSize.getWidth() * fBitmapScaling); + const double fHeight(aPrefSize.getHeight() * fBitmapScaling); + + if(basegfx::fTools::more(fWidth, 1.0) + && basegfx::fTools::more(fHeight, 1.0) + && basegfx::fTools::lessOrEqual(fWidth, aScale.getX()) + && basegfx::fTools::lessOrEqual(fHeight, aScale.getY())) + { + basegfx::B2DHomMatrix aBitmapMatrix; + + aBitmapMatrix.scale(fWidth, fHeight); + aBitmapMatrix.shearX(fShearX); + aBitmapMatrix.rotate(fRotate); + aBitmapMatrix.translate(aTranslate.getX(), aTranslate.getY()); + + drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(xRetval, + drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::BitmapPrimitive2D( + BitmapEx(aDraftBitmap), + aBitmapMatrix))); + + // consume bitmap size in X + aScale.setX(std::max(0.0, aScale.getX() - (fWidth + fDistance))); + aTranslate.setX(aTranslate.getX() + fWidth + fDistance); + } + } + + // Build the text for the draft object + XubString aDraftText = GetGrafObject().GetFileName(); + + if(!aDraftText.Len()) + { + aDraftText = GetGrafObject().GetName(); + aDraftText.AppendAscii(" ..."); + } + + if(aDraftText.Len() && GetGrafObject().GetModel()) + { + // #i103255# Goal is to produce TextPrimitives which hold the given text as + // BlockText in the available space. It would be very tricky to do + // an own word wrap/line layout here. + // Using SdrBlockTextPrimitive2D OTOH is critical since it internally + // uses the SdrObject it references. To solve this, create a temp + // SdrObject with Attributes and Text, generate a SdrBlockTextPrimitive2D + // directly and immediately decompose it. After that, it is no longer + // needed and can be deleted. + + // create temp RectObj as TextObj and set needed attributes + SdrRectObj aRectObj(OBJ_TEXT); + aRectObj.SetModel(GetGrafObject().GetModel()); + aRectObj.NbcSetText(aDraftText); + aRectObj.SetMergedItem(SvxColorItem(Color(COL_LIGHTRED), EE_CHAR_COLOR)); + + // get SdrText and OPO + SdrText* pSdrText = aRectObj.getText(0); + OutlinerParaObject* pOPO = aRectObj.GetOutlinerParaObject(); + + if(pSdrText && pOPO) + { + // directly use the remaining space as TextRangeTransform + basegfx::B2DHomMatrix aTextRangeTransform; + + aTextRangeTransform.scale(aScale.getX(), aScale.getY()); + aTextRangeTransform.shearX(fShearX); + aTextRangeTransform.rotate(fRotate); + aTextRangeTransform.translate(aTranslate.getX(), aTranslate.getY()); + + // directly create temp SdrBlockTextPrimitive2D + drawinglayer::primitive2d::SdrBlockTextPrimitive2D aBlockTextPrimitive( + *pSdrText, + *pOPO, + aTextRangeTransform, + false, + false, + false); + + // decompose immediately with neutral ViewInformation. This will + // layout the text to more simple TextPrimitives from drawinglayer + const drawinglayer::geometry::ViewInformation2D aViewInformation2D(0); + + drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence( + xRetval, + aBlockTextPrimitive.get2DDecomposition(aViewInformation2D)); + } + } + + return xRetval; + } + drawinglayer::primitive2d::Primitive2DSequence ViewContactOfGraphic::createViewIndependentPrimitive2DSequence() const { drawinglayer::primitive2d::Primitive2DSequence xRetval; @@ -86,7 +330,8 @@ namespace sdr if(pSdrText) { const SfxItemSet& rItemSet = GetGrafObject().GetMergedItemSet(); - drawinglayer::attribute::SdrLineFillShadowTextAttribute* pAttribute = drawinglayer::primitive2d::createNewSdrLineFillShadowTextAttribute(rItemSet, *pSdrText); + drawinglayer::attribute::SdrLineFillShadowTextAttribute* pAttribute = + drawinglayer::primitive2d::createNewSdrLineFillShadowTextAttribute(rItemSet, *pSdrText); bool bVisible(pAttribute && pAttribute->isVisible()); // create and fill GraphicAttr @@ -131,7 +376,7 @@ namespace sdr // which will use the primitive data we just create in the near future const Rectangle& rRectangle = GetGrafObject().GetGeoRect(); const ::basegfx::B2DRange aObjectRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom()); - ::basegfx::B2DHomMatrix aObjectMatrix; + basegfx::B2DHomMatrix aObjectMatrix; // look for mirroring const GeoStat& rGeoStat(GetGrafObject().GetGeoStat()); @@ -170,60 +415,25 @@ namespace sdr // get the current, unchenged graphic obect from SdrGrafObj const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject(false); - if(GetGrafObject().IsEmptyPresObj()) + if(visualisationUsesPresObj()) { // it's an EmptyPresObj, create the SdrGrafPrimitive2D without content and another scaled one // with the content which is the placeholder graphic - GraphicObject aEmptyGraphicObject; - GraphicAttr aEmptyGraphicAttr; - drawinglayer::attribute::SdrLineFillShadowTextAttribute aEmptyAttributes(0, 0, 0, 0, 0, 0); - - // SdrGrafPrimitive2D without content in original size which carries all eventual attributes and texts - const drawinglayer::primitive2d::Primitive2DReference xReferenceA(new drawinglayer::primitive2d::SdrGrafPrimitive2D( - aObjectMatrix, *pAttribute, aEmptyGraphicObject, aEmptyGraphicAttr)); - xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReferenceA, 1); - - // SdrGrafPrimitive2D with content (which is the preview graphic) scaled to smaller size and - // without attributes - basegfx::B2DHomMatrix aSmallerMatrix; - - // #i94431# for some reason, i forgot to take the PrefMapMode of the graphic - // into account. Since EmptyPresObj's are only used in Draw/Impress, it is - // safe to assume 100th mm as target. - Size aPrefSize(GetGrafObject().GetGrafPrefSize()); - - if(MAP_PIXEL == GetGrafObject().GetGrafPrefMapMode().GetMapUnit()) - { - aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MAP_100TH_MM); - } - else - { - aPrefSize = Application::GetDefaultDevice()->LogicToLogic(aPrefSize, GetGrafObject().GetGrafPrefMapMode(), MAP_100TH_MM); - } - - const double fOffsetX((aObjectRange.getWidth() - aPrefSize.getWidth()) / 2.0); - const double fOffsetY((aObjectRange.getHeight() - aPrefSize.getHeight()) / 2.0); - - if(basegfx::fTools::moreOrEqual(fOffsetX, 0.0) && basegfx::fTools::moreOrEqual(fOffsetY, 0.0)) - { - aSmallerMatrix.scale(aPrefSize.getWidth(), aPrefSize.getHeight()); - aSmallerMatrix.translate(fOffsetX, fOffsetY); - aSmallerMatrix.shearX(fShearX); - aSmallerMatrix.rotate(fRotate); - aSmallerMatrix.translate(aObjectRange.getMinX(), aObjectRange.getMinY()); - - const drawinglayer::primitive2d::Primitive2DReference xReferenceB(new drawinglayer::primitive2d::SdrGrafPrimitive2D( - aSmallerMatrix, - aEmptyAttributes, - rGraphicObject, - aLocalGrafInfo)); - - drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(xRetval, xReferenceB); - } + xRetval = createVIP2DSForPresObj(aObjectMatrix, *pAttribute, aLocalGrafInfo); + } + else if(visualisationUsesDraft()) + { + // #i102380# The graphic is swapped out. To not force a swap-in here, there is a mechanism + // which shows a swapped-out-visualisation (which gets created here now) and an asynchronious + // visual update mechanism for swapped-out grapgics when they were loaded (see AsynchGraphicLoadingEvent + // and ViewObjectContactOfGraphic implementation). Not forcing the swap-in here allows faster + // (non-blocking) processing here and thus in the effect e.g. fast scrolling through pages + xRetval = createVIP2DSForDraft(aObjectMatrix, *pAttribute); } else { - // create primitive + // create primitive. Info: Calling the copy-constructor of GraphicObject in this + // SdrGrafPrimitive2D constructor will force a full swap-in of the graphic const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::SdrGrafPrimitive2D( aObjectMatrix, *pAttribute, @@ -241,6 +451,31 @@ namespace sdr return xRetval; } + bool ViewContactOfGraphic::visualisationUsesPresObj() const + { + return GetGrafObject().IsEmptyPresObj(); + } + + bool ViewContactOfGraphic::visualisationUsesDraft() const + { + // no draft when already PresObj + if(visualisationUsesPresObj()) + return false; + + // draft when swapped out + const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject(false); + static bool bAllowReplacements(true); + + if(rGraphicObject.IsSwappedOut() && bAllowReplacements) + return true; + + // draft when no graphic + if(GRAPHIC_NONE == rGraphicObject.GetType() || GRAPHIC_DEFAULT == rGraphicObject.GetType()) + return true; + + return false; + } + } // end of namespace contact } // end of namespace sdr diff --git a/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx b/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx index da467f8c63..1cb99de835 100644 --- a/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx +++ b/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx @@ -62,59 +62,86 @@ namespace sdr if(pSdrText) { - drawinglayer::attribute::SdrLineFillShadowTextAttribute* pAttribute = drawinglayer::primitive2d::createNewSdrLineFillShadowTextAttribute(rItemSet, *pSdrText); + drawinglayer::attribute::SdrLineFillShadowTextAttribute* pAttribute = + drawinglayer::primitive2d::createNewSdrLineFillShadowTextAttribute( + rItemSet, + *pSdrText); if(pAttribute) { if(pAttribute->isVisible()) { - // prepare object transformation and unit polygon (direct model data) - ::basegfx::B2DHomMatrix aObjectMatrix; - ::basegfx::B2DPolyPolygon aUnitPolyPolygon(GetPathObj().GetPathPoly()); - const bool bIsLine( - !aUnitPolyPolygon.areControlPointsUsed() - && 1L == aUnitPolyPolygon.count() - && 2L == aUnitPolyPolygon.getB2DPolygon(0L).count()); - - if(bIsLine) - { - // special handling for single line mode (2 points) - const ::basegfx::B2DPolygon aSubPolygon(aUnitPolyPolygon.getB2DPolygon(0L)); - const ::basegfx::B2DPoint aStart(aSubPolygon.getB2DPoint(0L)); - const ::basegfx::B2DPoint aEnd(aSubPolygon.getB2DPoint(1L)); - const ::basegfx::B2DVector aLine(aEnd - aStart); - - // create new polygon - ::basegfx::B2DPolygon aNewPolygon; - aNewPolygon.append(::basegfx::B2DPoint(0.0, 0.0)); - aNewPolygon.append(::basegfx::B2DPoint(aLine.getLength(), 0.0)); - aUnitPolyPolygon.setB2DPolygon(0L, aNewPolygon); - - // fill objectMatrix with rotation and offset (no shear for lines, scale in polygon) - aObjectMatrix.rotate(atan2(aLine.getY(), aLine.getX())); - aObjectMatrix.translate(aStart.getX(), aStart.getY()); - } - else + basegfx::B2DPolyPolygon aUnitPolyPolygon(GetPathObj().GetPathPoly()); + const sal_uInt32 nPolyCount(aUnitPolyPolygon.count()); + + if(nPolyCount) { - // create scaled, but unsheared, unrotated and untranslated polygon - // by creating the object matrix and back-transforming the polygon - const ::basegfx::B2DRange aObjectRange(::basegfx::tools::getRange(aUnitPolyPolygon)); - const GeoStat& rGeoStat(GetPathObj().GetGeoStat()); - - aObjectMatrix.shearX(tan((36000 - rGeoStat.nShearWink) * F_PI18000)); - aObjectMatrix.rotate((36000 - rGeoStat.nDrehWink) * F_PI18000); - aObjectMatrix.translate(aObjectRange.getMinX(), aObjectRange.getMinY()); - - // ceate scaled unit polygon from object's absolute path - ::basegfx::B2DHomMatrix aInverse(aObjectMatrix); - aInverse.invert(); - aUnitPolyPolygon.transform(aInverse); + // prepare object transformation and unit polygon (direct model data) + basegfx::B2DHomMatrix aObjectMatrix; + const bool bIsLine( + !aUnitPolyPolygon.areControlPointsUsed() + && 1 == nPolyCount + && 2 == aUnitPolyPolygon.getB2DPolygon(0).count()); + + if(bIsLine) + { + // special handling for single line mode (2 points) + const basegfx::B2DPolygon aSubPolygon(aUnitPolyPolygon.getB2DPolygon(0)); + const basegfx::B2DPoint aStart(aSubPolygon.getB2DPoint(0)); + const basegfx::B2DPoint aEnd(aSubPolygon.getB2DPoint(1)); + const basegfx::B2DVector aLine(aEnd - aStart); + + // #i102548# create new unit polygon for line (horizontal) + basegfx::B2DPolygon aNewPolygon; + aNewPolygon.append(basegfx::B2DPoint(0.0, 0.0)); + aNewPolygon.append(basegfx::B2DPoint(1.0, 0.0)); + aUnitPolyPolygon.setB2DPolygon(0, aNewPolygon); + + // #i102548# fill objectMatrix with rotation and offset (no shear for lines) + aObjectMatrix.scale(aLine.getLength(), 1.0); + aObjectMatrix.rotate(atan2(aLine.getY(), aLine.getX())); + aObjectMatrix.translate(aStart.getX(), aStart.getY()); + } + else + { + // #i102548# create unscaled, unsheared, unrotated and untranslated polygon + // (unit polygon) by creating the object matrix and back-transforming the polygon + const basegfx::B2DRange aObjectRange(basegfx::tools::getRange(aUnitPolyPolygon)); + const GeoStat& rGeoStat(GetPathObj().GetGeoStat()); + const double fWidth(aObjectRange.getWidth()); + const double fHeight(aObjectRange.getHeight()); + + aObjectMatrix.scale( + basegfx::fTools::equalZero(fWidth) ? 1.0 : fWidth, + basegfx::fTools::equalZero(fHeight) ? 1.0 : fHeight); + + if(rGeoStat.nShearWink) + { + aObjectMatrix.shearX(tan((36000 - rGeoStat.nShearWink) * F_PI18000)); + } + + if(rGeoStat.nDrehWink) + { + aObjectMatrix.rotate((36000 - rGeoStat.nDrehWink) * F_PI18000); + } + + aObjectMatrix.translate(aObjectRange.getMinX(), aObjectRange.getMinY()); + + // ceate unit polygon from object's absolute path + basegfx::B2DHomMatrix aInverse(aObjectMatrix); + aInverse.invert(); + aUnitPolyPolygon.transform(aInverse); + } + + // create primitive + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrPathPrimitive2D( + aObjectMatrix, + *pAttribute, + aUnitPolyPolygon)); + + xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1); } - - // create primitive - const drawinglayer::primitive2d::Primitive2DReference xReference( - new drawinglayer::primitive2d::SdrPathPrimitive2D(aObjectMatrix, *pAttribute, aUnitPolyPolygon)); - xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1); } delete pAttribute; diff --git a/svx/source/sdr/contact/viewobjectcontactofgraphic.cxx b/svx/source/sdr/contact/viewobjectcontactofgraphic.cxx index 7a6e115490..4b3f2b6244 100644 --- a/svx/source/sdr/contact/viewobjectcontactofgraphic.cxx +++ b/svx/source/sdr/contact/viewobjectcontactofgraphic.cxx @@ -38,6 +38,7 @@ #include <svx/svdograf.hxx> #include <svx/sdr/contact/objectcontact.hxx> #include <svx/svdmodel.hxx> +#include <svx/svdpage.hxx> ////////////////////////////////////////////////////////////////////////////// @@ -245,10 +246,19 @@ namespace sdr { // prepare primitive generation with evtl. loading the graphic when it's swapped out SdrGrafObj& rGrafObj = const_cast< ViewObjectContactOfGraphic* >(this)->getSdrGrafObj(); - const bool bDoAsynchronGraphicLoading(rGrafObj.GetModel() && rGrafObj.GetModel()->IsSwapGraphics()); + bool bDoAsynchronGraphicLoading(rGrafObj.GetModel() && rGrafObj.GetModel()->IsSwapGraphics()); static bool bSuppressAsynchLoading(false); bool bSwapInDone(false); + if(bDoAsynchronGraphicLoading + && rGrafObj.IsSwappedOut() + && rGrafObj.GetPage() + && rGrafObj.GetPage()->IsMasterPage()) + { + // #i102380# force Swap-In for GraphicObjects on MasterPage to have a nicer visualisation + bDoAsynchronGraphicLoading = false; + } + if(bDoAsynchronGraphicLoading && !bSuppressAsynchLoading) { bSwapInDone = const_cast< ViewObjectContactOfGraphic* >(this)->impPrepareGraphicWithAsynchroniousLoading(); @@ -261,6 +271,23 @@ namespace sdr // get return value by calling parent drawinglayer::primitive2d::Primitive2DSequence xRetval = ViewObjectContactOfSdrObj::createPrimitive2DSequence(rDisplayInfo); + if(xRetval.hasElements()) + { + // #i103255# suppress when graphic needs draft visualisation and output + // is for PDF export/Printer + const ViewContactOfGraphic& rVCOfGraphic = static_cast< const ViewContactOfGraphic& >(GetViewContact()); + + if(rVCOfGraphic.visualisationUsesDraft()) + { + const ObjectContact& rObjectContact = GetObjectContact(); + + if(rObjectContact.isOutputToPDFFile() || rObjectContact.isOutputToPrinter()) + { + xRetval = drawinglayer::primitive2d::Primitive2DSequence(); + } + } + } + // if swap in was forced only for printing, swap out again const bool bSwapInExclusiveForPrinting(bSwapInDone && GetObjectContact().isOutputToPrinter()); diff --git a/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx b/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx index f69f4d5550..87ba0b8c3a 100644 --- a/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx +++ b/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx @@ -291,7 +291,8 @@ namespace sdr } // add a gray outline frame, except not when printing - if(!GetObjectContact().isOutputToPrinter()) + // #i102637# add frame also when printing and page exists (handout pages) + if(!GetObjectContact().isOutputToPrinter() || pPage) { const Color aFrameColor(aColorConfig.GetColorValue(svtools::OBJECTBOUNDARIES).nColor); basegfx::B2DPolygon aOwnOutline(basegfx::tools::createPolygonFromRect(basegfx::B2DRange(0.0, 0.0, 1.0, 1.0))); diff --git a/svx/source/sdr/properties/properties.cxx b/svx/source/sdr/properties/properties.cxx index 0d6cb3b83b..fe317a495a 100644 --- a/svx/source/sdr/properties/properties.cxx +++ b/svx/source/sdr/properties/properties.cxx @@ -180,6 +180,11 @@ namespace sdr GetSdrObject().SendUserCall(SDRUSERCALL_CHGATTR, rChange.GetRectangle(a)); } } + + sal_uInt32 BaseProperties::getVersion() const + { + return 0; + } } // end of namespace properties } // end of namespace sdr diff --git a/svx/source/sdr/properties/textproperties.cxx b/svx/source/sdr/properties/textproperties.cxx index 22ddd794d7..06e151d2a4 100644 --- a/svx/source/sdr/properties/textproperties.cxx +++ b/svx/source/sdr/properties/textproperties.cxx @@ -72,12 +72,14 @@ namespace sdr } TextProperties::TextProperties(SdrObject& rObj) - : AttributeProperties(rObj) + : AttributeProperties(rObj), + maVersion(0) { } TextProperties::TextProperties(const TextProperties& rProps, SdrObject& rObj) - : AttributeProperties(rProps, rObj) + : AttributeProperties(rProps, rObj), + maVersion(rProps.getVersion()) { } @@ -93,10 +95,11 @@ namespace sdr void TextProperties::ItemSetChanged(const SfxItemSet& rSet) { SdrTextObj& rObj = (SdrTextObj&)GetSdrObject(); - - sal_Int32 nText = rObj.getTextCount(); + // #i101556# ItemSet has changed -> new version + maVersion++; + while( --nText >= 0 ) { SdrText* pText = rObj.getText( nText ); @@ -237,6 +240,9 @@ namespace sdr // call parent AttributeProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr); + // #i101556# StyleSheet has changed -> new version + maVersion++; + if( rObj.GetModel() && !rObj.IsTextEditActive() && !rObj.IsLinkedText() ) { SdrOutliner& rOutliner = rObj.ImpGetDrawOutliner(); @@ -577,6 +583,9 @@ namespace sdr rObj.ActionChanged(); //rObj.BroadcastObjectChange(); } + + // #i101556# content of StyleSheet has changed -> new version + maVersion++; } if(SFX_HINT_DYING == nId) @@ -616,6 +625,12 @@ namespace sdr } } } + + // #i101556# Handout version information + sal_uInt32 TextProperties::getVersion() const + { + return maVersion; + } } // end of namespace properties } // end of namespace sdr diff --git a/svx/source/svdraw/svdcrtv.cxx b/svx/source/svdraw/svdcrtv.cxx index d0d2cd01d2..2ef952ad82 100644 --- a/svx/source/svdraw/svdcrtv.cxx +++ b/svx/source/svdraw/svdcrtv.cxx @@ -821,13 +821,15 @@ void SdrCreateView::ShowCreateObj(/*OutputDevice* pOut, BOOL bFull*/) } } - // #i68562# Force to non-solid dragging when not creating a full circle and up to step three - if(bUseSolidDragging - && pAktCreate->ISA(SdrCircObj) - && OBJ_CIRC != (SdrObjKind)(static_cast< SdrCircObj* >(pAktCreate)->GetObjIdentifier()) - && aDragStat.GetPointAnz() < 4L) + // #i101781# force to non-solid dragging when not creating a full circle + if(bUseSolidDragging) { - bUseSolidDragging = false; + SdrCircObj* pCircObj = dynamic_cast< SdrCircObj* >(pAktCreate); + + if(pCircObj && OBJ_CIRC != pCircObj->GetObjIdentifier()) + { + bUseSolidDragging = false; + } } if(bUseSolidDragging) diff --git a/svx/source/svdraw/svdobj.cxx b/svx/source/svdraw/svdobj.cxx index 7aa383576f..fecc43056f 100644 --- a/svx/source/svdraw/svdobj.cxx +++ b/svx/source/svdraw/svdobj.cxx @@ -1167,6 +1167,11 @@ basegfx::B2DPolyPolygon SdrObject::TakeContour() const SfxItemSet aNewSet(*GetObjectItemPool()); + // #i101980# ignore LineWidth; that's what the old implementation + // did. With linewidth, the result may be huge due to fat/thick + // line decompositions + aNewSet.Put(XLineWidthItem(0)); + // solid black lines and no fill aNewSet.Put(XLineStyleItem(XLINE_SOLID)); aNewSet.Put(XLineColorItem(String(), Color(COL_BLACK))); diff --git a/svx/source/svdraw/svdograf.cxx b/svx/source/svdraw/svdograf.cxx index a7e9d7e67f..1bf8cebe72 100644 --- a/svx/source/svdraw/svdograf.cxx +++ b/svx/source/svdraw/svdograf.cxx @@ -666,6 +666,23 @@ void SdrGrafObj::TakeObjNamePlural( XubString& rName ) const // ----------------------------------------------------------------------------- +SdrObject* SdrGrafObj::getFullDragClone() const +{ + // call parent + SdrGrafObj* pRetval = static_cast< SdrGrafObj* >(SdrRectObj::getFullDragClone()); + + // #i103116# the full drag clone leads to problems + // with linked graphics, so reset the link in this + // temporary interaction object and load graphic + if(pRetval && IsLinkedGraphic()) + { + pRetval->ForceSwapIn(); + pRetval->ReleaseGraphicLink(); + } + + return pRetval; +} + void SdrGrafObj::operator=( const SdrObject& rObj ) { SdrRectObj::operator=( rObj ); @@ -1097,6 +1114,14 @@ IMPL_LINK( SdrGrafObj, ImpSwapHdl, GraphicObject*, pO ) pRet = GRFMGR_AUTOSWAPSTREAM_TEMP; pGraphic->SetUserData(); } + + // #i102380# + sdr::contact::ViewContactOfGraphic* pVC = dynamic_cast< sdr::contact::ViewContactOfGraphic* >(&GetViewContact()); + + if(pVC) + { + pVC->flushGraphicObjects(); + } } } } |