diff --git a/itext.tests/itext.svg.tests/itext/svg/renderers/OpacityTest.cs b/itext.tests/itext.svg.tests/itext/svg/renderers/OpacityTest.cs index a3c9f6c51..dac31926f 100644 --- a/itext.tests/itext.svg.tests/itext/svg/renderers/OpacityTest.cs +++ b/itext.tests/itext.svg.tests/itext/svg/renderers/OpacityTest.cs @@ -60,9 +60,7 @@ public virtual void TestRGBA() { [NUnit.Framework.Test] public virtual void TestFillOpacityWithComma() { - //TODO DEVSIX-2678 SVG: Displaying invalid value of fill-opacity incorrectly - NUnit.Framework.Assert.Catch(typeof(FormatException), () => ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER - , "testFillOpacityWithComma")); + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "testFillOpacityWithComma"); } [NUnit.Framework.Test] @@ -72,15 +70,12 @@ public virtual void TestFillOpacityWithPercents() { [NUnit.Framework.Test] public virtual void TestFillOpacity() { - //TODO: update after DEVSIX-2678 fix ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "svg_fill_opacity"); } [NUnit.Framework.Test] public virtual void TestStrokeOpacityWithComma() { - //TODO DEVSIX-2679 - NUnit.Framework.Assert.Catch(typeof(Exception), () => ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, - "testStrokeOpacityWithComma")); + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "testStrokeOpacityWithComma"); } [NUnit.Framework.Test] @@ -90,7 +85,6 @@ public virtual void TestStrokeOpacityWithPercents() { [NUnit.Framework.Test] public virtual void TestStrokeOpacity() { - //TODO: update after DEVSIX-2679 fix ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "svg_stroke_opacity"); } } diff --git a/itext.tests/itext.svg.tests/itext/svg/renderers/VectorEffectTest.cs b/itext.tests/itext.svg.tests/itext/svg/renderers/VectorEffectTest.cs new file mode 100644 index 000000000..398e32f18 --- /dev/null +++ b/itext.tests/itext.svg.tests/itext/svg/renderers/VectorEffectTest.cs @@ -0,0 +1,108 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2025 Apryse Group NV +Authors: Apryse Software. + +This program is offered under a commercial and under the AGPL license. +For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below. + +AGPL licensing: +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +using System; +using iText.Svg.Logs; +using iText.Test; +using iText.Test.Attributes; + +namespace iText.Svg.Renderers { + [NUnit.Framework.Category("IntegrationTest")] + public class VectorEffectTest : SvgIntegrationTest { + private static readonly String SOURCE_FOLDER = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext + .CurrentContext.TestDirectory) + "/resources/itext/svg/renderers/VectorEffectTest/"; + + private static readonly String DESTINATION_FOLDER = NUnit.Framework.TestContext.CurrentContext.TestDirectory + + "/test/itext/svg/renderers/VectorEffectTest/"; + + [NUnit.Framework.OneTimeSetUp] + public static void BeforeClass() { + ITextTest.CreateDestinationFolder(DESTINATION_FOLDER); + } + + [NUnit.Framework.Test] + public virtual void NonScalingStrokeTest() { + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "nonScalingStroke"); + } + + [NUnit.Framework.Test] + public virtual void NonScalingStrokePathTest() { + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "nonScalingStrokePath"); + } + + [NUnit.Framework.Test] + public virtual void NonScalingStrokeFiguresTest() { + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "nonScalingStrokeFigures"); + } + + [NUnit.Framework.Test] + public virtual void NonScalingStrokeTextTest() { + // TODO DEVSIX-8850 support vector-effect for text and tspan + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "nonScalingStrokeText"); + } + + [NUnit.Framework.Test] + public virtual void PreserveAspectRatioTest() { + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "preserveAspectRatio"); + } + + [NUnit.Framework.Test] + public virtual void SvgWithSvgTest() { + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "svgWithSvg"); + } + + [NUnit.Framework.Test] + public virtual void SeveralTransformationsTest() { + // TODO DEVSIX-8850 support vector-effect for text and tspan + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "severalTransformations"); + } + + [NUnit.Framework.Test] + public virtual void SeveralNestedSvgTest() { + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "severalNestedSvg"); + } + + [NUnit.Framework.Test] + [LogMessage(iText.StyledXmlParser.Logs.StyledXmlParserLogMessageConstant.UNABLE_TO_RETRIEVE_IMAGE_WITH_GIVEN_BASE_URI + )] + public virtual void ImageWithSvgTest() { + // TODO DEVSIX-8884 Support svg format for image href attribute + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "imageWithSvg"); + } + + [NUnit.Framework.Test] + public virtual void ClipPathTest() { + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "clipPath"); + } + + [NUnit.Framework.Test] + public virtual void VectorEffectOnUseTest() { + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "vectorEffectOnUse"); + } + + [NUnit.Framework.Test] + [LogMessage(SvgLogMessageConstant.NON_INVERTIBLE_TRANSFORMATION_MATRIX_FOR_NON_SCALING_STROKE)] + public virtual void NonInvertibleMatrixTest() { + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "nonInvertibleMatrix"); + } + } +} diff --git a/itext.tests/itext.svg.tests/itext/svg/renderers/impl/ClipPathSvgNodeRendererIntegrationTest.cs b/itext.tests/itext.svg.tests/itext/svg/renderers/impl/ClipPathSvgNodeRendererIntegrationTest.cs index 81f4943b6..3f34b642d 100644 --- a/itext.tests/itext.svg.tests/itext/svg/renderers/impl/ClipPathSvgNodeRendererIntegrationTest.cs +++ b/itext.tests/itext.svg.tests/itext/svg/renderers/impl/ClipPathSvgNodeRendererIntegrationTest.cs @@ -42,6 +42,16 @@ public static void BeforeClass() { ITextTest.CreateDestinationFolder(DESTINATION_FOLDER); } + [NUnit.Framework.Test] + public virtual void EmptyClipPathTest() { + ConvertAndCompareSinglePage(SOURCE_FOLDER, DESTINATION_FOLDER, "emptyClipPath"); + } + + [NUnit.Framework.Test] + public virtual void InvalidClipPathTest() { + ConvertAndCompareSinglePage(SOURCE_FOLDER, DESTINATION_FOLDER, "invalidClipPath"); + } + [NUnit.Framework.Test] public virtual void RectClipPathComplexTest() { ConvertAndCompareSinglePage(SOURCE_FOLDER, DESTINATION_FOLDER, "clippath_rect_complex"); diff --git a/itext.tests/itext.svg.tests/itext/svg/renderers/impl/ImageSvgNodeRendererIntegrationTest.cs b/itext.tests/itext.svg.tests/itext/svg/renderers/impl/ImageSvgNodeRendererIntegrationTest.cs index 2e91ec564..4d8b3089f 100644 --- a/itext.tests/itext.svg.tests/itext/svg/renderers/impl/ImageSvgNodeRendererIntegrationTest.cs +++ b/itext.tests/itext.svg.tests/itext/svg/renderers/impl/ImageSvgNodeRendererIntegrationTest.cs @@ -54,6 +54,11 @@ public virtual void SingleImageTest() { ConvertAndCompareSinglePage(sourceFolder, destinationFolder, "singleImage", properties); } + [NUnit.Framework.Test] + public virtual void SingleImageHrefTest() { + ConvertAndCompareSinglePage(sourceFolder, destinationFolder, "singleImageHref", properties); + } + [NUnit.Framework.Test] public virtual void ImageWithRectangleTest() { ConvertAndCompareSinglePage(sourceFolder, destinationFolder, "imageWithRectangle", properties); diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgBarChartFromFileTest.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgBarChartFromFileTest.pdf index 8b4070709..5c2b05618 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgBarChartFromFileTest.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgBarChartFromFileTest.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgBarChartFromStringTest.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgBarChartFromStringTest.pdf index 5d59de601..9f432631e 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgBarChartFromStringTest.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgBarChartFromStringTest.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgPieChartFromFileTest.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgPieChartFromFileTest.pdf index f0670160d..df2fd47e7 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgPieChartFromFileTest.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgPieChartFromFileTest.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/css/SvgStyleResolver/cmp_chartWithText1.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/css/SvgStyleResolver/cmp_chartWithText1.pdf index be6e4dc53..bd29fbb72 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/css/SvgStyleResolver/cmp_chartWithText1.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/css/SvgStyleResolver/cmp_chartWithText1.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/IntervalsChartsTest/cmp_intervalsBackgroundBoxChart.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/IntervalsChartsTest/cmp_intervalsBackgroundBoxChart.pdf index 3d770f15b..bb5c01b54 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/IntervalsChartsTest/cmp_intervalsBackgroundBoxChart.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/IntervalsChartsTest/cmp_intervalsBackgroundBoxChart.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/SteppedAreaChartsTest/cmp_steppedArea2Chart.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/SteppedAreaChartsTest/cmp_steppedArea2Chart.pdf index 28881216f..aab8b7028 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/SteppedAreaChartsTest/cmp_steppedArea2Chart.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/SteppedAreaChartsTest/cmp_steppedArea2Chart.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/SteppedAreaChartsTest/cmp_steppedAreaChart.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/SteppedAreaChartsTest/cmp_steppedAreaChart.pdf index fa6866cf4..bda9755a9 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/SteppedAreaChartsTest/cmp_steppedAreaChart.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/googlecharts/SteppedAreaChartsTest/cmp_steppedAreaChart.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/clipPath.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/clipPath.svg new file mode 100644 index 000000000..463888da4 --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/clipPath.svg @@ -0,0 +1,53 @@ + + + + + + use + + + + + + + + + + + + + + + + + + + + + + + + text + text + + + tspan + + tspan + + + + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_clipPath.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_clipPath.pdf new file mode 100644 index 000000000..6a07662c2 Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_clipPath.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_imageWithSvg.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_imageWithSvg.pdf new file mode 100644 index 000000000..e29a48913 Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_imageWithSvg.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonInvertibleMatrix.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonInvertibleMatrix.pdf new file mode 100644 index 000000000..845ec4bfc Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonInvertibleMatrix.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStroke.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStroke.pdf new file mode 100644 index 000000000..930ac93c9 Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStroke.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStrokeFigures.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStrokeFigures.pdf new file mode 100644 index 000000000..9d419c8ef Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStrokeFigures.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStrokePath.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStrokePath.pdf new file mode 100644 index 000000000..dcd10285d Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStrokePath.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStrokeText.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStrokeText.pdf new file mode 100644 index 000000000..899891c2b Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_nonScalingStrokeText.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_preserveAspectRatio.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_preserveAspectRatio.pdf new file mode 100644 index 000000000..cd7c15d20 Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_preserveAspectRatio.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_severalNestedSvg.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_severalNestedSvg.pdf new file mode 100644 index 000000000..2b68a6f8e Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_severalNestedSvg.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_severalTransformations.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_severalTransformations.pdf new file mode 100644 index 000000000..67830cbef Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_severalTransformations.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_svgWithSvg.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_svgWithSvg.pdf new file mode 100644 index 000000000..2a11d1368 Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_svgWithSvg.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_vectorEffectOnUse.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_vectorEffectOnUse.pdf new file mode 100644 index 000000000..3b975c102 Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/cmp_vectorEffectOnUse.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/image.png b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/image.png new file mode 100644 index 000000000..2ffed0aff Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/image.png differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/imageWithSvg.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/imageWithSvg.svg new file mode 100644 index 000000000..6330efb8b --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/imageWithSvg.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonInvertibleMatrix.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonInvertibleMatrix.svg new file mode 100644 index 000000000..f2757b08d --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonInvertibleMatrix.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStroke.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStroke.svg new file mode 100644 index 000000000..e19689236 --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStroke.svg @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStrokeFigures.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStrokeFigures.svg new file mode 100644 index 000000000..9aed284f9 --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStrokeFigures.svg @@ -0,0 +1,60 @@ + + + + + + use + + + + Example non-scaling stroke + + + + + + + + + + + + + + + + + + + + + + + + + text + text + + + tspan + + tspan + + + + + + + + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStrokePath.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStrokePath.svg new file mode 100644 index 000000000..2360d9051 --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStrokePath.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStrokeText.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStrokeText.svg new file mode 100644 index 000000000..025009735 --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/nonScalingStrokeText.svg @@ -0,0 +1,48 @@ + + + Example non-scaling stroke in text and tspan + + + + + 1 + 2 + + 2+ve + + 3 + + 4 + + + 4+ve + + + + 3+ve + + + + + 1+ve + 2 + + 2+ve + + 3 + + 4 + + + 4+ve + + + + 3+ve + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/preserveAspectRatio.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/preserveAspectRatio.svg new file mode 100644 index 000000000..60dc4acc3 --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/preserveAspectRatio.svg @@ -0,0 +1,61 @@ + + + + + + use + + + + Example non-scaling stroke + + + + + + + + + + + + + + + + + + + + + + + + + text + text + + + tspan + + tspan + + + + + + + + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/severalNestedSvg.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/severalNestedSvg.svg new file mode 100644 index 000000000..6017468e1 --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/severalNestedSvg.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + A + + + + B + + B + + + + + + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/severalTransformations.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/severalTransformations.svg new file mode 100644 index 000000000..65e1a6586 --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/severalTransformations.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + + B + + C + + + A + + B + + C + + + + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/svgWithSvg.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/svgWithSvg.svg new file mode 100644 index 000000000..cc5e5b15b --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/svgWithSvg.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + A + + + B + + B + + + + + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/vectorEffectOnUse.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/vectorEffectOnUse.svg new file mode 100644 index 000000000..da4f9f7ac --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/VectorEffectTest/vectorEffectOnUse.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/cmp_emptyClipPath.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/cmp_emptyClipPath.pdf new file mode 100644 index 000000000..9aba1573d Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/cmp_emptyClipPath.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/cmp_invalidClipPath.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/cmp_invalidClipPath.pdf new file mode 100644 index 000000000..4db4a82f6 Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/cmp_invalidClipPath.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/emptyClipPath.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/emptyClipPath.svg new file mode 100644 index 000000000..ae1e5341a --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/emptyClipPath.svg @@ -0,0 +1,8 @@ + + + + + + + text + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/invalidClipPath.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/invalidClipPath.svg new file mode 100644 index 000000000..84f3529e4 --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ClipPathTest/invalidClipPath.svg @@ -0,0 +1,8 @@ + + + + + + + text + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ImageSvgNodeRendererTest/cmp_singleImageHref.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ImageSvgNodeRendererTest/cmp_singleImageHref.pdf new file mode 100644 index 000000000..f0c879e2f Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ImageSvgNodeRendererTest/cmp_singleImageHref.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ImageSvgNodeRendererTest/singleImageHref.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ImageSvgNodeRendererTest/singleImageHref.svg new file mode 100644 index 000000000..36235cdfc --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/ImageSvgNodeRendererTest/singleImageHref.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/cmp_testFillOpacityWithComma.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/cmp_testFillOpacityWithComma.pdf new file mode 100644 index 000000000..3071c72ef Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/cmp_testFillOpacityWithComma.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/cmp_testStrokeOpacityWithComma.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/cmp_testStrokeOpacityWithComma.pdf new file mode 100644 index 000000000..656d9cea0 Binary files /dev/null and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/cmp_testStrokeOpacityWithComma.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/testStrokeOpacityWithComma.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/testStrokeOpacityWithComma.svg new file mode 100644 index 000000000..04f0346bb --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/testStrokeOpacityWithComma.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/testStrokeOpacityWithPercents.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/testStrokeOpacityWithPercents.svg index 18752fbe2..240b1e244 100644 --- a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/testStrokeOpacityWithPercents.svg +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/OpacityTest/testStrokeOpacityWithPercents.svg @@ -1,12 +1,12 @@ - - - - - - - - - + + + + + + + + + diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicCircularRoundedRxRectangle.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicCircularRoundedRxRectangle.pdf index ebf0ed0c3..6c125da21 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicCircularRoundedRxRectangle.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicCircularRoundedRxRectangle.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicCircularRoundedRyRectangle.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicCircularRoundedRyRectangle.pdf index cb6f3ec96..f5c6c9eed 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicCircularRoundedRyRectangle.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicCircularRoundedRyRectangle.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicEllipticalNegativeHeightRoundedRectangle.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicEllipticalNegativeHeightRoundedRectangle.pdf index eb8840a43..8ec1f1776 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicEllipticalNegativeHeightRoundedRectangle.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicEllipticalNegativeHeightRoundedRectangle.pdf differ diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicEllipticalNegativeWidthRoundedRectangle.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicEllipticalNegativeWidthRoundedRectangle.pdf index 24d05d888..0c25a497a 100644 Binary files a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicEllipticalNegativeWidthRoundedRectangle.pdf and b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/RectangleSvgNodeRendererTest/cmp_basicEllipticalNegativeWidthRoundedRectangle.pdf differ diff --git a/itext/itext.kernel/itext/kernel/pdf/canvas/PdfCanvas.cs b/itext/itext.kernel/itext/kernel/pdf/canvas/PdfCanvas.cs index 5e885767d..39d3a34c7 100644 --- a/itext/itext.kernel/itext/kernel/pdf/canvas/PdfCanvas.cs +++ b/itext/itext.kernel/itext/kernel/pdf/canvas/PdfCanvas.cs @@ -1052,14 +1052,35 @@ public virtual iText.Kernel.Pdf.Canvas.PdfCanvas Rectangle(iText.Kernel.Geom.Rec } /// Draws rounded rectangle. - /// x coordinate of the starting point. - /// y coordinate of the starting point. - /// width. - /// height. - /// radius of the arc corner. - /// current canvas. + /// x coordinate of the starting point + /// y coordinate of the starting point + /// width + /// height + /// radius of the arc corner + /// current canvas public virtual iText.Kernel.Pdf.Canvas.PdfCanvas RoundRectangle(double x, double y, double width, double height , double radius) { + return RoundRectangle(x, y, width, height, radius, radius, null); + } + + /// Draws rounded rectangle. + /// x coordinate of the starting point + /// y coordinate of the starting point + /// width + /// height + /// x radius of the arc corner + /// y radius of the arc corner + /// + /// + /// + /// to apply before drawing, + /// or + /// + /// in case transform shouldn't be applied + /// + /// current canvas + public virtual iText.Kernel.Pdf.Canvas.PdfCanvas RoundRectangle(double x, double y, double width, double height + , double rx, double ry, AffineTransform transform) { if (width < 0) { x += width; width = -width; @@ -1068,20 +1089,22 @@ public virtual iText.Kernel.Pdf.Canvas.PdfCanvas RoundRectangle(double x, double y += height; height = -height; } - if (radius < 0) { - radius = -radius; + if (rx < 0) { + rx = -rx; } - double curv = 0.4477f; - MoveTo(x + radius, y); - LineTo(x + width - radius, y); - CurveTo(x + width - radius * curv, y, x + width, y + radius * curv, x + width, y + radius); - LineTo(x + width, y + height - radius); - CurveTo(x + width, y + height - radius * curv, x + width - radius * curv, y + height, x + width - radius, - y + height); - LineTo(x + radius, y + height); - CurveTo(x + radius * curv, y + height, x, y + height - radius * curv, x, y + height - radius); - LineTo(x, y + radius); - CurveTo(x, y + radius * curv, x + radius * curv, y, x + radius, y); + if (ry < 0) { + ry = -ry; + } + double[] points = GetEllipseRoundedRectPoints(x, y, width, height, rx, ry); + if (transform != null) { + transform.Transform(points, 0, points, 0, points.Length / 2); + } + int i = 0; + this.MoveTo(points[i++], points[i++]).LineTo(points[i++], points[i++]).CurveTo(points[i++], points[i++], points + [i++], points[i++], points[i++], points[i++]).LineTo(points[i++], points[i++]).CurveTo(points[i++], points + [i++], points[i++], points[i++], points[i++], points[i++]).LineTo(points[i++], points[i++]).CurveTo(points + [i++], points[i++], points[i++], points[i++], points[i++], points[i++]).LineTo(points[i++], points[i++ + ]).CurveTo(points[i++], points[i++], points[i++], points[i++], points[i++], points[i]).ClosePath(); return this; } @@ -2505,6 +2528,37 @@ private static bool IsIdentityMatrix(float a, float b, float c, float d, float e && Math.Abs(1 - d) < IDENTITY_MATRIX_EPS && Math.Abs(e) < IDENTITY_MATRIX_EPS && Math.Abs(f) < IDENTITY_MATRIX_EPS; } + private static double[] GetEllipseRoundedRectPoints(double x, double y, double width, double height, double + rx, double ry) { + /* + + y+h -> ____________________________ + / \ + / \ + y+h-ry -> / \ + | | + | | + | | + | | + y+ry -> \ / + \ / + y -> \____________________________/ + ^ ^ ^ ^ + x x+rx x+w-rx x+w + + */ + double[] pt1 = iText.Kernel.Pdf.Canvas.PdfCanvas.BezierArc(x + width - 2 * rx, y, x + width, y + 2 * ry, - + 90, 90)[0]; + double[] pt2 = iText.Kernel.Pdf.Canvas.PdfCanvas.BezierArc(x + width, y + height - 2 * ry, x + width - 2 * + rx, y + height, 0, 90)[0]; + double[] pt3 = iText.Kernel.Pdf.Canvas.PdfCanvas.BezierArc(x + 2 * rx, y + height, x, y + height - 2 * ry, + 90, 90)[0]; + double[] pt4 = iText.Kernel.Pdf.Canvas.PdfCanvas.BezierArc(x, y + 2 * ry, x + 2 * rx, y, 180, 90)[0]; + return new double[] { x + rx, y, x + width - rx, y, pt1[2], pt1[3], pt1[4], pt1[5], pt1[6], pt1[7], x + width + , y + height - ry, pt2[2], pt2[3], pt2[4], pt2[5], pt2[6], pt2[7], x + rx, y + height, pt3[2], pt3[3], + pt3[4], pt3[5], pt3[6], pt3[7], x, y + ry, pt4[2], pt4[3], pt4[4], pt4[5], pt4[6], pt4[7] }; + } + /// This method is used to traverse parent tree and begin all layers in it. /// /// This method is used to traverse parent tree and begin all layers in it. diff --git a/itext/itext.svg/itext/svg/SvgConstants.cs b/itext/itext.svg/itext/svg/SvgConstants.cs index 5fae5e4a4..b56195798 100644 --- a/itext/itext.svg/itext/svg/SvgConstants.cs +++ b/itext/itext.svg/itext/svg/SvgConstants.cs @@ -606,7 +606,10 @@ public sealed class Attributes : CommonAttributeConstants { /// Attribute defining the second y coordinate value of a line. public const String Y2 = "y2"; - /// Attribute defining version + /// Attribute defining vector-effect. + public const String VECTOR_EFFECT = "vector-effect"; + + /// Attribute defining version. public const String VERSION = "version"; } @@ -657,9 +660,12 @@ public sealed class Values { /// Value representing the meet for preserve aspect ratio calculations. public const String MEET = "meet"; - /// Value representing the "none" value". + /// Value representing the "none" value. public const String NONE = "none"; + /// Value representing the "non-scaling-stroke" value for vector-effect attribute. + public const String NONE_SCALING_STROKE = "non-scaling-stroke"; + /// Value representing the units relation "objectBoundingBox". public const String OBJECT_BOUNDING_BOX = "objectBoundingBox"; diff --git a/itext/itext.svg/itext/svg/logs/SvgLogMessageConstant.cs b/itext/itext.svg/itext/svg/logs/SvgLogMessageConstant.cs index 88f8d78ff..c8f0e237b 100644 --- a/itext/itext.svg/itext/svg/logs/SvgLogMessageConstant.cs +++ b/itext/itext.svg/itext/svg/logs/SvgLogMessageConstant.cs @@ -62,6 +62,9 @@ public sealed class SvgLogMessageConstant { public const String NONINVERTIBLE_TRANSFORMATION_MATRIX_USED_IN_CLIP_PATH = "Non-invertible transformation matrix was used in a clipping path context. Clipped elements may show " + "undefined behavior."; + public const String NON_INVERTIBLE_TRANSFORMATION_MATRIX_FOR_NON_SCALING_STROKE = "Unable to get inverse transformation matrix and thus apply non-scaling-stroke vector-effect property: " + + "some of the transformation matrices, written to the document, have a determinant of zero value."; + public const String UNABLE_TO_GET_INVERSE_MATRIX_DUE_TO_ZERO_DETERMINANT = "Unable to get inverse transformation matrix and thus calculate a viewport for the element because some of" + " the transformation matrices, which are written to document, have a determinant of zero value. " + "A bbox of zero values will be used as a viewport for this element."; diff --git a/itext/itext.svg/itext/svg/renderers/SvgDrawContext.cs b/itext/itext.svg/itext/svg/renderers/SvgDrawContext.cs index f2d5a5f00..f2aba534e 100644 --- a/itext/itext.svg/itext/svg/renderers/SvgDrawContext.cs +++ b/itext/itext.svg/itext/svg/renderers/SvgDrawContext.cs @@ -452,5 +452,28 @@ public virtual AffineTransform GetClippingElementTransform() { public virtual void ResetClippingElementTransform() { this.clippingElementTransform.SetToIdentity(); } + + /// Concatenates all transformations applied from the top level of the svg to the current one. + /// + /// + /// + /// instance + /// + public virtual AffineTransform GetConcatenatedTransform() { + IList canvasList = new List(); + int canvasesSize = this.Size(); + for (int i = 0; i < canvasesSize; i++) { + canvasList.Add(this.PopCanvas()); + } + AffineTransform transform = new AffineTransform(); + for (int i = canvasList.Count - 1; i >= 0; i--) { + PdfCanvas pdfCanvas = canvasList[i]; + Matrix matrix = pdfCanvas.GetGraphicsState().GetCtm(); + transform.Concatenate(new AffineTransform(matrix.Get(0), matrix.Get(1), matrix.Get(3), matrix.Get(4), matrix + .Get(6), matrix.Get(7))); + this.PushCanvas(pdfCanvas); + } + return transform; + } } } diff --git a/itext/itext.svg/itext/svg/renderers/impl/AbstractBranchSvgNodeRenderer.cs b/itext/itext.svg/itext/svg/renderers/impl/AbstractBranchSvgNodeRenderer.cs index 42ba91668..dc605616c 100644 --- a/itext/itext.svg/itext/svg/renderers/impl/AbstractBranchSvgNodeRenderer.cs +++ b/itext/itext.svg/itext/svg/renderers/impl/AbstractBranchSvgNodeRenderer.cs @@ -269,19 +269,7 @@ private static bool IsOverflowVisible(AbstractSvgNodeRenderer currentElement) { /// bbox /// private static Rectangle GetBBoxAccordingToVisibleOverflow(SvgDrawContext context) { - IList canvases = new List(); - int canvasesSize = context.Size(); - for (int i = 0; i < canvasesSize; i++) { - canvases.Add(context.PopCanvas()); - } - AffineTransform transform = new AffineTransform(); - for (int i = canvases.Count - 1; i >= 0; i--) { - PdfCanvas canvas = canvases[i]; - Matrix matrix = canvas.GetGraphicsState().GetCtm(); - transform.Concatenate(new AffineTransform(matrix.Get(0), matrix.Get(1), matrix.Get(3), matrix.Get(4), matrix - .Get(6), matrix.Get(7))); - context.PushCanvas(canvas); - } + AffineTransform transform = context.GetConcatenatedTransform(); try { transform = transform.CreateInverse(); } diff --git a/itext/itext.svg/itext/svg/renderers/impl/AbstractSvgNodeRenderer.cs b/itext/itext.svg/itext/svg/renderers/impl/AbstractSvgNodeRenderer.cs index 4379741c6..3661779f9 100644 --- a/itext/itext.svg/itext/svg/renderers/impl/AbstractSvgNodeRenderer.cs +++ b/itext/itext.svg/itext/svg/renderers/impl/AbstractSvgNodeRenderer.cs @@ -22,6 +22,8 @@ You should have received a copy of the GNU Affero General Public License */ using System; using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using iText.Commons; using iText.Kernel.Colors; using iText.Kernel.Geom; using iText.Kernel.Pdf.Canvas; @@ -34,6 +36,7 @@ You should have received a copy of the GNU Affero General Public License using iText.Svg; using iText.Svg.Css; using iText.Svg.Css.Impl; +using iText.Svg.Logs; using iText.Svg.Renderers; using iText.Svg.Utils; @@ -46,6 +49,8 @@ public abstract class AbstractSvgNodeRenderer : ISvgNodeRenderer { private static readonly MarkerVertexType[] MARKER_VERTEX_TYPES = new MarkerVertexType[] { MarkerVertexType .MARKER_START, MarkerVertexType.MARKER_MID, MarkerVertexType.MARKER_END }; + private static readonly ILogger LOGGER = ITextLogManager.GetLogger(typeof(AbstractSvgNodeRenderer)); + /// Map that contains attributes and styles used for drawing operations. protected internal IDictionary attributesAndStyles; @@ -275,6 +280,34 @@ internal virtual ClipPathSvgNodeRenderer GetParentClipPath() { } //\endcond +//\cond DO_NOT_DOCUMENT + /// + /// Applies non-scaling-stroke vector-effect to this renderer by concatenating all transformations applied + /// from the top level of the svg to the current one, inverting it and applying to the current canvas. + /// + /// the SVG draw context + /// + /// the transformation that was inverted and applied to this renderer + /// to achieve non-scaling-stroke vector-effect + /// + internal virtual AffineTransform ApplyNonScalingStrokeTransform(SvgDrawContext context) { + AffineTransform transform = null; + bool isNonScalingStroke = doStroke && SvgConstants.Values.NONE_SCALING_STROKE.Equals(GetAttribute(SvgConstants.Attributes + .VECTOR_EFFECT)); + if (isNonScalingStroke) { + transform = context.GetConcatenatedTransform(); + try { + context.GetCurrentCanvas().ConcatMatrix(transform.CreateInverse()); + } + catch (NoninvertibleTransformException) { + LOGGER.LogWarning(SvgLogMessageConstant.NON_INVERTIBLE_TRANSFORMATION_MATRIX_FOR_NON_SCALING_STROKE); + transform = null; + } + } + return transform; + } +//\endcond + //\cond DO_NOT_DOCUMENT /// Calculate the transformation for the viewport based on the context. /// @@ -587,7 +620,12 @@ private float GetOpacityByAttributeName(String attributeName, float generalOpaci opacityValue = CssDimensionParsingUtils.ParseRelativeValue(opacityStr, 1f); } else { - opacityValue = float.Parse(opacityStr, System.Globalization.CultureInfo.InvariantCulture); + if (CssTypesValidationUtils.IsNumber(opacityStr)) { + opacityValue = (float)CssDimensionParsingUtils.ParseFloat(opacityStr); + } + else { + opacityValue = 1; + } } opacity *= opacityValue; } @@ -605,7 +643,7 @@ private bool DrawInClipPath(SvgDrawContext context) { SvgNodeRendererInheritanceResolver.ApplyInheritanceToSubTree(this, clipPath, context.GetCssContext()); clipPath.SetClippedRenderer(this); clipPath.Draw(context); - return !clipPath.GetChildren().IsEmpty(); + return true; } } return false; diff --git a/itext/itext.svg/itext/svg/renderers/impl/EllipseSvgNodeRenderer.cs b/itext/itext.svg/itext/svg/renderers/impl/EllipseSvgNodeRenderer.cs index bc0ebfa96..ee528994b 100644 --- a/itext/itext.svg/itext/svg/renderers/impl/EllipseSvgNodeRenderer.cs +++ b/itext/itext.svg/itext/svg/renderers/impl/EllipseSvgNodeRenderer.cs @@ -54,9 +54,14 @@ protected internal override void DoDraw(SvgDrawContext context) { cv.WriteLiteral("% ellipse\n"); if (SetParameters(context)) { // Use double type locally to have better precision of the result after applying arithmetic operations - cv.MoveTo((double)cx + (double)rx, cy); + double[] startPoint = new double[] { (double)cx + (double)rx, cy }; + AffineTransform transform = ApplyNonScalingStrokeTransform(context); + if (transform != null) { + transform.Transform(startPoint, 0, startPoint, 0, startPoint.Length / 2); + } + cv.MoveTo(startPoint[0], startPoint[1]); DrawUtils.Arc((double)cx - (double)rx, (double)cy - (double)ry, (double)cx + (double)rx, (double)cy + (double - )ry, 0, 360, cv); + )ry, 0, 360, cv, transform); } } diff --git a/itext/itext.svg/itext/svg/renderers/impl/ImageSvgNodeRenderer.cs b/itext/itext.svg/itext/svg/renderers/impl/ImageSvgNodeRenderer.cs index ad40bde70..79b1bad65 100644 --- a/itext/itext.svg/itext/svg/renderers/impl/ImageSvgNodeRenderer.cs +++ b/itext/itext.svg/itext/svg/renderers/impl/ImageSvgNodeRenderer.cs @@ -50,7 +50,10 @@ protected internal override void DoDraw(SvgDrawContext context) { if (resourceResolver == null || this.attributesAndStyles == null) { return; } - String uri = this.attributesAndStyles.Get(SvgConstants.Attributes.XLINK_HREF); + String uri = this.attributesAndStyles.Get(SvgConstants.Attributes.HREF); + if (uri == null) { + uri = this.attributesAndStyles.Get(SvgConstants.Attributes.XLINK_HREF); + } PdfXObject xObject = resourceResolver.RetrieveImage(uri); if (xObject == null) { return; diff --git a/itext/itext.svg/itext/svg/renderers/impl/LineSvgNodeRenderer.cs b/itext/itext.svg/itext/svg/renderers/impl/LineSvgNodeRenderer.cs index 839e018a8..82dc5fd25 100644 --- a/itext/itext.svg/itext/svg/renderers/impl/LineSvgNodeRenderer.cs +++ b/itext/itext.svg/itext/svg/renderers/impl/LineSvgNodeRenderer.cs @@ -47,7 +47,13 @@ protected internal override void DoDraw(SvgDrawContext context) { PdfCanvas canvas = context.GetCurrentCanvas(); canvas.WriteLiteral("% line\n"); if (SetParameters(context)) { - canvas.MoveTo(x1, y1).LineTo(x2, y2); + float[] points = new float[] { x1, y1, x2, y2 }; + AffineTransform transform = ApplyNonScalingStrokeTransform(context); + if (transform != null) { + transform.Transform(points, 0, points, 0, points.Length / 2); + } + int i = 0; + canvas.MoveTo(points[i++], points[i++]).LineTo(points[i++], points[i]); } } diff --git a/itext/itext.svg/itext/svg/renderers/impl/PathSvgNodeRenderer.cs b/itext/itext.svg/itext/svg/renderers/impl/PathSvgNodeRenderer.cs index 626a81510..dd43f9aa9 100644 --- a/itext/itext.svg/itext/svg/renderers/impl/PathSvgNodeRenderer.cs +++ b/itext/itext.svg/itext/svg/renderers/impl/PathSvgNodeRenderer.cs @@ -110,11 +110,13 @@ public class PathSvgNodeRenderer : AbstractSvgNodeRenderer, IMarkerCapable { protected internal override void DoDraw(SvgDrawContext context) { PdfCanvas canvas = context.GetCurrentCanvas(); canvas.WriteLiteral("% path\n"); + AffineTransform transform = ApplyNonScalingStrokeTransform(context); foreach (IPathShape item in GetShapes()) { if (item is AbstractPathShape) { AbstractPathShape shape = (AbstractPathShape)item; shape.SetParent(this); shape.SetContext(context); + shape.SetTransform(transform); } item.Draw(context.GetCurrentCanvas()); } diff --git a/itext/itext.svg/itext/svg/renderers/impl/PolylineSvgNodeRenderer.cs b/itext/itext.svg/itext/svg/renderers/impl/PolylineSvgNodeRenderer.cs index 05a22a835..b1356aed3 100644 --- a/itext/itext.svg/itext/svg/renderers/impl/PolylineSvgNodeRenderer.cs +++ b/itext/itext.svg/itext/svg/renderers/impl/PolylineSvgNodeRenderer.cs @@ -22,6 +22,7 @@ You should have received a copy of the GNU Affero General Public License */ using System; using System.Collections.Generic; +using iText.Commons.Utils; using iText.Kernel.Geom; using iText.Kernel.Pdf.Canvas; using iText.StyledXmlParser.Css.Util; @@ -120,6 +121,12 @@ protected internal override void DoDraw(SvgDrawContext context) { PdfCanvas canvas = context.GetCurrentCanvas(); canvas.WriteLiteral("% polyline\n"); if (points.Count > 1) { + AffineTransform transform = ApplyNonScalingStrokeTransform(context); + if (transform != null) { + Point[] pt = points.ToArray(new Point[0]); + transform.Transform(pt, 0, pt, 0, pt.Length); + points = JavaUtil.ArraysAsList(pt); + } Point currentPoint = points[0]; canvas.MoveTo(currentPoint.GetX(), currentPoint.GetY()); for (int x = 1; x < points.Count; x++) { diff --git a/itext/itext.svg/itext/svg/renderers/impl/RectangleSvgNodeRenderer.cs b/itext/itext.svg/itext/svg/renderers/impl/RectangleSvgNodeRenderer.cs index 347c46222..b6fc371d8 100644 --- a/itext/itext.svg/itext/svg/renderers/impl/RectangleSvgNodeRenderer.cs +++ b/itext/itext.svg/itext/svg/renderers/impl/RectangleSvgNodeRenderer.cs @@ -59,46 +59,31 @@ protected internal override void DoDraw(SvgDrawContext context) { cv.WriteLiteral("% rect\n"); SetParameters(context); bool singleValuePresent = (rxPresent && !ryPresent) || (!rxPresent && ryPresent); + AffineTransform transform = ApplyNonScalingStrokeTransform(context); if (!rxPresent && !ryPresent) { - cv.Rectangle(x, y, width, height); + Point[] points = new Rectangle(x, y, width, height).ToPointsArray(); + if (transform != null) { + transform.Transform(points, 0, points, 0, points.Length); + if (Math.Abs(transform.GetShearX()) > 0 || Math.Abs(transform.GetShearY()) > 0) { + int i = 0; + cv.MoveTo(points[i].GetX(), points[i++].GetY()).LineTo(points[i].GetX(), points[i++].GetY()).LineTo(points + [i].GetX(), points[i++].GetY()).LineTo(points[i].GetX(), points[i].GetY()).ClosePath(); + return; + } + } + cv.Rectangle(points[0].GetX(), points[0].GetY(), points[1].GetX() - points[0].GetX(), points[2].GetY() - points + [0].GetY()); } else { if (singleValuePresent) { cv.WriteLiteral("% circle rounded rect\n"); - // only look for radius in case of circular rounding + // Only look for radius in case of circular rounding. float radius = FindCircularRadius(rx, ry, width, height); - cv.RoundRectangle(x, y, width, height, radius); + cv.RoundRectangle(x, y, width, height, radius, radius, transform); } else { cv.WriteLiteral("% ellipse rounded rect\n"); - // TODO (DEVSIX-1878): this should actually be refactored into PdfCanvas.roundRectangle() - /* - - y+h -> ____________________________ - / \ - / \ - y+h-ry -> / \ - | | - | | - | | - | | - y+ry -> \ / - \ / - y -> \____________________________/ - ^ ^ ^ ^ - x x+rx x+w-rx x+w - - */ - cv.MoveTo(x + rx, y); - cv.LineTo(x + width - rx, y); - Arc(x + width - 2 * rx, y, x + width, y + 2 * ry, -90, 90, cv); - cv.LineTo(x + width, y + height - ry); - Arc(x + width, y + height - 2 * ry, x + width - 2 * rx, y + height, 0, 90, cv); - cv.LineTo(x + rx, y + height); - Arc(x + 2 * rx, y + height, x, y + height - 2 * ry, 90, 90, cv); - cv.LineTo(x, y + ry); - Arc(x, y + 2 * ry, x + 2 * rx, y, 180, 90, cv); - cv.ClosePath(); + cv.RoundRectangle(x, y, width, height, rx, ry, transform); } } } @@ -129,17 +114,6 @@ private void SetParameters(SvgDrawContext context) { } } - private void Arc(float x1, float y1, float x2, float y2, float startAng, float extent, PdfCanvas cv) { - IList ar = PdfCanvas.BezierArc(x1, y1, x2, y2, startAng, extent); - if (!ar.IsEmpty()) { - double[] pt; - for (int k = 0; k < ar.Count; ++k) { - pt = ar[k]; - cv.CurveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]); - } - } - } - //\cond DO_NOT_DOCUMENT /// /// a radius must be positive, and cannot be more than half the distance in diff --git a/itext/itext.svg/itext/svg/renderers/path/impl/AbstractPathShape.cs b/itext/itext.svg/itext/svg/renderers/path/impl/AbstractPathShape.cs index e9a6750e2..9bfadf237 100644 --- a/itext/itext.svg/itext/svg/renderers/path/impl/AbstractPathShape.cs +++ b/itext/itext.svg/itext/svg/renderers/path/impl/AbstractPathShape.cs @@ -35,6 +35,8 @@ namespace iText.Svg.Renderers.Path.Impl { public abstract class AbstractPathShape : IPathShape { private PathSvgNodeRenderer parent; + private AffineTransform transform = null; + /// The properties of this shape. protected internal IDictionary properties; @@ -109,6 +111,20 @@ public virtual void SetContext(SvgDrawContext context) { this.context = context; } + /// + /// Sets + /// + /// to apply before drawing the shape. + /// + /// + /// + /// + /// to apply before drawing + /// + public virtual void SetTransform(AffineTransform transform) { + this.transform = transform; + } + /// Parse x axis length value. /// /// @@ -131,6 +147,14 @@ protected internal virtual float ParseVerticalLength(String length) { return SvgCssUtils.ParseAbsoluteVerticalLength(parent, length, 0.0F, context); } +//\cond DO_NOT_DOCUMENT + internal virtual void ApplyTransform(double[] points) { + if (transform != null) { + transform.Transform(points, 0, points, 0, points.Length / 2); + } + } +//\endcond + public abstract void SetCoordinates(String[] arg1, Point arg2); } } diff --git a/itext/itext.svg/itext/svg/renderers/path/impl/CurveTo.cs b/itext/itext.svg/itext/svg/renderers/path/impl/CurveTo.cs index 57eb45488..86118446e 100644 --- a/itext/itext.svg/itext/svg/renderers/path/impl/CurveTo.cs +++ b/itext/itext.svg/itext/svg/renderers/path/impl/CurveTo.cs @@ -49,13 +49,17 @@ public CurveTo(bool relative, IOperatorConverter copier) } public override void Draw() { - float x1 = ParseHorizontalLength(coordinates[0]); - float y1 = ParseVerticalLength(coordinates[1]); - float x2 = ParseHorizontalLength(coordinates[2]); - float y2 = ParseVerticalLength(coordinates[3]); - float x = ParseHorizontalLength(coordinates[4]); - float y = ParseVerticalLength(coordinates[5]); - context.GetCurrentCanvas().CurveTo(x1, y1, x2, y2, x, y); + double x1 = ParseHorizontalLength(coordinates[0]); + double y1 = ParseVerticalLength(coordinates[1]); + double x2 = ParseHorizontalLength(coordinates[2]); + double y2 = ParseVerticalLength(coordinates[3]); + double x = ParseHorizontalLength(coordinates[4]); + double y = ParseVerticalLength(coordinates[5]); + double[] points = new double[] { x1, y1, x2, y2, x, y }; + ApplyTransform(points); + int i = 0; + context.GetCurrentCanvas().CurveTo(points[i++], points[i++], points[i++], points[i++], points[i++], points + [i]); } public override void SetCoordinates(String[] inputCoordinates, Point startPoint) { diff --git a/itext/itext.svg/itext/svg/renderers/path/impl/EllipticalCurveTo.cs b/itext/itext.svg/itext/svg/renderers/path/impl/EllipticalCurveTo.cs index 49d0448da..d25336962 100644 --- a/itext/itext.svg/itext/svg/renderers/path/impl/EllipticalCurveTo.cs +++ b/itext/itext.svg/itext/svg/renderers/path/impl/EllipticalCurveTo.cs @@ -99,7 +99,9 @@ public override void Draw() { /* edge case: If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto") * joining the endpoints. */ - context.GetCurrentCanvas().LineTo(end.GetX(), end.GetY()); + double[] points = new double[] { end.GetX(), end.GetY() }; + ApplyTransform(points); + context.GetCurrentCanvas().LineTo(points[0], points[1]); } else { /* This is the first step of calculating a rotated elliptical path. @@ -166,8 +168,11 @@ internal virtual String[] GetCoordinates() { } //\endcond - private static void DrawCurve(PdfCanvas canvas, Point cp1, Point cp2, Point end) { - canvas.CurveTo(cp1.GetX(), cp1.GetY(), cp2.GetX(), cp2.GetY(), end.GetX(), end.GetY()); + private void DrawCurve(PdfCanvas canvas, Point cp1, Point cp2, Point end) { + double[] points = new double[] { cp1.GetX(), cp1.GetY(), cp2.GetX(), cp2.GetY(), end.GetX(), end.GetY() }; + ApplyTransform(points); + int i = 0; + canvas.CurveTo(points[i++], points[i++], points[i++], points[i++], points[i++], points[i]); } private Point[][] MakePoints(IList input) { diff --git a/itext/itext.svg/itext/svg/renderers/path/impl/LineTo.cs b/itext/itext.svg/itext/svg/renderers/path/impl/LineTo.cs index 1107951fc..f3261e073 100644 --- a/itext/itext.svg/itext/svg/renderers/path/impl/LineTo.cs +++ b/itext/itext.svg/itext/svg/renderers/path/impl/LineTo.cs @@ -41,9 +41,11 @@ public LineTo(bool relative) } public override void Draw() { - float x = ParseHorizontalLength(coordinates[0]); - float y = ParseVerticalLength(coordinates[1]); - context.GetCurrentCanvas().LineTo(x, y); + double x = ParseHorizontalLength(coordinates[0]); + double y = ParseVerticalLength(coordinates[1]); + double[] points = new double[] { x, y }; + ApplyTransform(points); + context.GetCurrentCanvas().LineTo(points[0], points[1]); } public override void SetCoordinates(String[] inputCoordinates, Point startPoint) { diff --git a/itext/itext.svg/itext/svg/renderers/path/impl/MoveTo.cs b/itext/itext.svg/itext/svg/renderers/path/impl/MoveTo.cs index d4e1acf76..96f9aaa6d 100644 --- a/itext/itext.svg/itext/svg/renderers/path/impl/MoveTo.cs +++ b/itext/itext.svg/itext/svg/renderers/path/impl/MoveTo.cs @@ -41,9 +41,11 @@ public MoveTo(bool relative) } public override void Draw() { - float x = ParseHorizontalLength(coordinates[0]); - float y = ParseVerticalLength(coordinates[1]); - context.GetCurrentCanvas().MoveTo(x, y); + double x = ParseHorizontalLength(coordinates[0]); + double y = ParseVerticalLength(coordinates[1]); + double[] points = new double[] { x, y }; + ApplyTransform(points); + context.GetCurrentCanvas().MoveTo(points[0], points[1]); } public override void SetCoordinates(String[] inputCoordinates, Point startPoint) { diff --git a/itext/itext.svg/itext/svg/renderers/path/impl/QuadraticCurveTo.cs b/itext/itext.svg/itext/svg/renderers/path/impl/QuadraticCurveTo.cs index d6dacaa6e..bba158921 100644 --- a/itext/itext.svg/itext/svg/renderers/path/impl/QuadraticCurveTo.cs +++ b/itext/itext.svg/itext/svg/renderers/path/impl/QuadraticCurveTo.cs @@ -49,11 +49,14 @@ public QuadraticCurveTo(bool relative, IOperatorConverter copier) /// Draws a quadratic Bezier curve from the current point to (x,y) using (x1,y1) as the control point /// public override void Draw() { - float x1 = ParseHorizontalLength(coordinates[0]); - float y1 = ParseVerticalLength(coordinates[1]); - float x = ParseHorizontalLength(coordinates[2]); - float y = ParseVerticalLength(coordinates[3]); - context.GetCurrentCanvas().CurveTo(x1, y1, x, y); + double x1 = ParseHorizontalLength(coordinates[0]); + double y1 = ParseVerticalLength(coordinates[1]); + double x = ParseHorizontalLength(coordinates[2]); + double y = ParseVerticalLength(coordinates[3]); + double[] points = new double[] { x1, y1, x, y }; + ApplyTransform(points); + int i = 0; + context.GetCurrentCanvas().CurveTo(points[i++], points[i++], points[i++], points[i]); } public override void SetCoordinates(String[] inputCoordinates, Point startPoint) { diff --git a/itext/itext.svg/itext/svg/utils/DrawUtils.cs b/itext/itext.svg/itext/svg/utils/DrawUtils.cs index 830d6934a..7c41a039e 100644 --- a/itext/itext.svg/itext/svg/utils/DrawUtils.cs +++ b/itext/itext.svg/itext/svg/utils/DrawUtils.cs @@ -22,6 +22,7 @@ You should have received a copy of the GNU Affero General Public License */ using System; using System.Collections.Generic; +using iText.Kernel.Geom; using iText.Kernel.Pdf.Canvas; using iText.Svg; @@ -52,9 +53,41 @@ private DrawUtils() { /// canvas to paint on public static void Arc(double x1, double y1, double x2, double y2, double startAng, double extent, PdfCanvas cv) { + Arc(x1, y1, x2, y2, startAng, extent, cv, null); + } + + /// + /// Draw an arc on the passed canvas with provided transform, + /// enclosed by the rectangle for which two opposite corners are specified. + /// + /// + /// Draw an arc on the passed canvas with provided transform, + /// enclosed by the rectangle for which two opposite corners are specified. + /// The arc starts at the passed starting angle and extends to the starting angle + extent + /// + /// corner-coordinate of the enclosing rectangle, first corner + /// corner-coordinate of the enclosing rectangle, first corner + /// corner-coordinate of the enclosing rectangle, second corner + /// corner-coordinate of the enclosing rectangle, second corner + /// starting angle in degrees + /// extent of the arc + /// canvas to paint on + /// + /// + /// + /// to apply before drawing, + /// or + /// + /// in case transform shouldn't be applied + /// + public static void Arc(double x1, double y1, double x2, double y2, double startAng, double extent, PdfCanvas + cv, AffineTransform transform) { IList ar = PdfCanvas.BezierArc(x1, y1, x2, y2, startAng, extent); if (!ar.IsEmpty()) { foreach (double[] pt in ar) { + if (transform != null) { + transform.Transform(pt, 0, pt, 0, pt.Length / 2); + } cv.CurveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]); } } diff --git a/port-hash b/port-hash index 82e8c5e5e..3566ace11 100644 --- a/port-hash +++ b/port-hash @@ -1 +1 @@ -ce35dbce51bd8c3f1b01e78313f1f5a2a02201ff +cbb82b1d822c93fa6b76c4fb7b35c2e4182ea809