diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..a480df6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,40 @@ +# iText Security Policy + +## Reporting a Vulnerability + +We are committed to maintaining the security of our software. If you discover a security vulnerability, we encourage you to report it to us as soon as possible. + +To report a vulnerability, please visit our [Vulnerability Reporting Page](https://itextpdf.com/report-vulnerability), or email [vulnerability@apryse.com](vulnerability@apryse.com). If you do not receive a response in 2 business days, please follow up as we may not have received your message. + +We follow the procedure of Coordinated Vulnerability Disclosure (CVD) and, to protect the ecosystem, we request that those reporting do the same. Please visit the above page for more information, and follow the steps below to ensure that your report is handled promptly and appropriately: + +1. **Do not disclose the vulnerability publicly** until we have had a chance to address it. +2. **Provide a detailed description** of the vulnerability, including steps to reproduce it, if possible. +3. **Include any relevant information** such as the version of pdfSweep you are using, your operating system, and any other pertinent details. + +## Security Updates and Patches + + When a vulnerability is reported, we will: + +1. **Investigate and verify** the vulnerability. +2. **Develop and test** a fix for the vulnerability. +3. **Release a patch** as soon as possible. + + +## Known Vulnerabilities + +The iText Knowledge Base has a page for known [Common Vulnerabilities and Exposures](https://kb.itextpdf.com/itext/cves) (CVEs), please check it to ensure your vulnerability has not already been disclosed or addressed. + +## Supported product lines + +See [Compatibility Matrix](https://kb.itextpdf.com/itext/compatibility-matrix) + +## Security Best Practices + +To help ensure the security of your applications using pdfSweep, we recommend the following best practices: + +1. **Keep pdfSweep up to date** by regularly checking for and applying updates. +2. **Review and follow** our security guidelines for secure usage. +3. **Monitor your applications** for any unusual activity and investigate any anomalies promptly. + +Thank you for helping us keep iText secure! diff --git a/doxyfile b/doxyfile index d589f16..35440d2 100644 --- a/doxyfile +++ b/doxyfile @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "pdfSweep 4.0.3 API" +PROJECT_NAME = "pdfSweep 5.0.0 API" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version diff --git a/itext.tests/itext.cleanup.tests/Properties/AssemblyInfo.cs b/itext.tests/itext.cleanup.tests/Properties/AssemblyInfo.cs index 898ca0f..b97f01b 100644 --- a/itext.tests/itext.cleanup.tests/Properties/AssemblyInfo.cs +++ b/itext.tests/itext.cleanup.tests/Properties/AssemblyInfo.cs @@ -15,8 +15,8 @@ [assembly: Guid("647c862e-d837-4901-8e7b-68f5f8bf8f34")] -[assembly: AssemblyVersion("4.0.3.0")] -[assembly: AssemblyFileVersion("4.0.3.0")] -[assembly: AssemblyInformationalVersion("4.0.3")] +[assembly: AssemblyVersion("5.0.0.0")] +[assembly: AssemblyFileVersion("5.0.0.0")] +[assembly: AssemblyInformationalVersion("5.0.0")] [assembly: NUnit.Framework.Timeout(300000)] diff --git a/itext.tests/itext.cleanup.tests/itext.cleanup.tests.csproj b/itext.tests/itext.cleanup.tests/itext.cleanup.tests.csproj index 3dd1ede..284a3dc 100644 --- a/itext.tests/itext.cleanup.tests/itext.cleanup.tests.csproj +++ b/itext.tests/itext.cleanup.tests/itext.cleanup.tests.csproj @@ -39,12 +39,12 @@ - + - + - + diff --git a/itext.tests/itext.cleanup.tests/itext/pdfcleanup/BigDocumentCleanUpTest.cs b/itext.tests/itext.cleanup.tests/itext/pdfcleanup/BigDocumentCleanUpTest.cs index 9c5f496..c81a680 100644 --- a/itext.tests/itext.cleanup.tests/itext/pdfcleanup/BigDocumentCleanUpTest.cs +++ b/itext.tests/itext.cleanup.tests/itext/pdfcleanup/BigDocumentCleanUpTest.cs @@ -65,6 +65,21 @@ public virtual void BigTaggedDocument() { CompareByContent(cmp, output, outputPath, "4"); } + [NUnit.Framework.Test] + [LogMessage(iText.IO.Logs.IoLogMessageConstant.CREATED_ROOT_TAG_HAS_MAPPING)] + public virtual void BigTaggedDocumentDynamicOffsetMultiplier() { + String input = inputPath + "chapter8_Interactive_features.pdf"; + String output = outputPath + "bigTaggedDocumentDynamicOffsetMultiplier.pdf"; + String cmp = inputPath + "cmp_bigTaggedDocument.pdf"; + IList rects = JavaUtil.ArraysAsList(new Rectangle(60f, 80f, 460f, 65f), new Rectangle(300f, 370f + , 215f, 270f)); + using (PdfDocument pdfDocument = new PdfDocument(new PdfReader(input), new PdfWriter(output))) { + PdfCleaner.CleanUp(pdfDocument, InitLocations(rects, 131), new CleanUpProperties().SetOffsetProperties(new + PathOffsetApproximationProperties().CalculateOffsetMultiplierDynamically(true))); + } + CompareByContent(cmp, output, outputPath, "4"); + } + [NUnit.Framework.Test] public virtual void TextPositioning() { String input = inputPath + "textPositioning.pdf"; diff --git a/itext.tests/itext.cleanup.tests/itext/pdfcleanup/CleanUpPropertiesUnitTest.cs b/itext.tests/itext.cleanup.tests/itext/pdfcleanup/CleanUpPropertiesUnitTest.cs index 9f56e5f..8135a42 100644 --- a/itext.tests/itext.cleanup.tests/itext/pdfcleanup/CleanUpPropertiesUnitTest.cs +++ b/itext.tests/itext.cleanup.tests/itext/pdfcleanup/CleanUpPropertiesUnitTest.cs @@ -81,5 +81,15 @@ public virtual void SettingAspectRatioToNullIsOk() { properties.SetOverlapRatio(null); NUnit.Framework.Assert.IsNull(properties.GetOverlapRatio()); } + + [NUnit.Framework.Test] + public virtual void SetGetPathOffsetApproximationPropertiesTest() { + PathOffsetApproximationProperties pathOffsetApproximationProperties = new PathOffsetApproximationProperties + ().CalculateOffsetMultiplierDynamically(true).SetArcTolerance(0.0015); + CleanUpProperties properties = new CleanUpProperties().SetOffsetProperties(pathOffsetApproximationProperties + ); + NUnit.Framework.Assert.IsTrue(properties.GetOffsetProperties().CalculateOffsetMultiplierDynamically()); + NUnit.Framework.Assert.AreEqual(0.0015, properties.GetOffsetProperties().GetArcTolerance()); + } } } diff --git a/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpFilterUnitTest.cs b/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpFilterUnitTest.cs index 5af47df..1573d8b 100644 --- a/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpFilterUnitTest.cs +++ b/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpFilterUnitTest.cs @@ -27,6 +27,86 @@ You should have received a copy of the GNU Affero General Public License namespace iText.PdfCleanup { [NUnit.Framework.Category("UnitTest")] public class PdfCleanUpFilterUnitTest : ExtendedITextTest { + [NUnit.Framework.Test] + public virtual void PointIntersectLineCaseTest1() { + Point[] intersectSubject = new Point[] { new Point(50, 60), new Point(70, 60), new Point(50, 60) }; + Point[] intersecting = new Point[] { new Point(50, 50), new Point(50, 70), new Point(50, 50) }; + PdfCleanUpFilter filter = new PdfCleanUpFilter(new List(), new CleanUpProperties()); + NUnit.Framework.Assert.IsTrue(filter.CheckIfRectanglesIntersect(intersectSubject, intersecting)); + } + + [NUnit.Framework.Test] + public virtual void PointIntersectLineCaseTest2() { + Point[] intersectSubject = new Point[] { new Point(50, 60), new Point(70, 60), new Point(50, 60) }; + Point[] intersecting = new Point[] { new Point(50, 50), new Point(50, 30), new Point(50, 50) }; + PdfCleanUpFilter filter = new PdfCleanUpFilter(new List(), new CleanUpProperties()); + NUnit.Framework.Assert.IsFalse(filter.CheckIfRectanglesIntersect(intersectSubject, intersecting)); + } + + [NUnit.Framework.Test] + public virtual void PointIntersectLineCaseTest3() { + Point[] intersectSubject = new Point[] { new Point(50, 65), new Point(70, 65), new Point(50, 65) }; + Point[] intersecting = new Point[] { new Point(40, 50), new Point(60, 70), new Point(40, 50) }; + PdfCleanUpFilter filter = new PdfCleanUpFilter(new List(), new CleanUpProperties()); + NUnit.Framework.Assert.IsFalse(filter.CheckIfRectanglesIntersect(intersectSubject, intersecting)); + } + + [NUnit.Framework.Test] + public virtual void PointIntersectLineCaseTest4() { + Point[] intersectSubject = new Point[] { new Point(50, 60), new Point(70, 60), new Point(50, 60) }; + Point[] intersecting = new Point[] { new Point(30, 50), new Point(70, 70), new Point(30, 50) }; + PdfCleanUpFilter filter = new PdfCleanUpFilter(new List(), new CleanUpProperties()); + NUnit.Framework.Assert.IsTrue(filter.CheckIfRectanglesIntersect(intersectSubject, intersecting)); + } + + [NUnit.Framework.Test] + public virtual void PointIntersectLineCaseTest5() { + Point[] intersectSubject = new Point[] { new Point(50, 60), new Point(70, 60), new Point(50, 60) }; + Point[] intersecting = new Point[] { new Point(70, 50), new Point(30, 70), new Point(70, 50) }; + PdfCleanUpFilter filter = new PdfCleanUpFilter(new List(), new CleanUpProperties()); + NUnit.Framework.Assert.IsTrue(filter.CheckIfRectanglesIntersect(intersectSubject, intersecting)); + } + + [NUnit.Framework.Test] + public virtual void PointIntersectLineCaseTest6() { + Point[] intersectSubject = new Point[] { new Point(50, 80), new Point(70, 80), new Point(50, 80) }; + Point[] intersecting = new Point[] { new Point(50, 50), new Point(50, 70), new Point(50, 50) }; + PdfCleanUpFilter filter = new PdfCleanUpFilter(new List(), new CleanUpProperties()); + NUnit.Framework.Assert.IsFalse(filter.CheckIfRectanglesIntersect(intersectSubject, intersecting)); + } + + [NUnit.Framework.Test] + public virtual void PointIntersectLineCaseTest7() { + Point[] intersectSubject = new Point[] { new Point(50, 40), new Point(70, 40), new Point(50, 40) }; + Point[] intersecting = new Point[] { new Point(50, 50), new Point(50, 70), new Point(50, 50) }; + PdfCleanUpFilter filter = new PdfCleanUpFilter(new List(), new CleanUpProperties()); + NUnit.Framework.Assert.IsFalse(filter.CheckIfRectanglesIntersect(intersectSubject, intersecting)); + } + + [NUnit.Framework.Test] + public virtual void PointIntersectLineCaseTest8() { + Point[] intersectSubject = new Point[] { new Point(50, 20), new Point(70, 20), new Point(50, 20) }; + Point[] intersecting = new Point[] { new Point(50, 50), new Point(50, 30), new Point(50, 50) }; + PdfCleanUpFilter filter = new PdfCleanUpFilter(new List(), new CleanUpProperties()); + NUnit.Framework.Assert.IsFalse(filter.CheckIfRectanglesIntersect(intersectSubject, intersecting)); + } + + [NUnit.Framework.Test] + public virtual void PointIntersectLineCaseTest9() { + Point[] intersectSubject = new Point[] { new Point(50, 40), new Point(70, 40), new Point(50, 40) }; + Point[] intersecting = new Point[] { new Point(50, 50), new Point(50, 30), new Point(50, 50) }; + PdfCleanUpFilter filter = new PdfCleanUpFilter(new List(), new CleanUpProperties()); + NUnit.Framework.Assert.IsTrue(filter.CheckIfRectanglesIntersect(intersectSubject, intersecting)); + } + + [NUnit.Framework.Test] + public virtual void PointIntersectLineCaseTest10() { + Point[] intersectSubject = new Point[] { new Point(30, 80), new Point(90, 80), new Point(30, 80) }; + Point[] intersecting = new Point[] { new Point(60, 50), new Point(40, 70), new Point(60, 50) }; + PdfCleanUpFilter filter = new PdfCleanUpFilter(new List(), new CleanUpProperties()); + NUnit.Framework.Assert.IsFalse(filter.CheckIfRectanglesIntersect(intersectSubject, intersecting)); + } + [NUnit.Framework.Test] public virtual void CheckIfRectanglesIntersect_completelyCoveredBasic() { Point[] intersectSubject = new Point[] { new Point(70, 70), new Point(80, 70), new Point(80, 80), new Point diff --git a/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpProcessorUnitTest.cs b/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpProcessorUnitTest.cs index 5822b1b..62d0fcd 100644 --- a/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpProcessorUnitTest.cs +++ b/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpProcessorUnitTest.cs @@ -144,15 +144,15 @@ public virtual void OpenNotWrittenTagsEmptyTest() { } private void TestOpenNotWrittenTags(LinkedList tags) { - PdfCleanUpProcessor processor = new _PdfCleanUpProcessor_161(tags, null, null); + PdfCleanUpProcessor processor = new _PdfCleanUpProcessor_160(tags, null, null); foreach (CanvasTag tag in tags) { processor.AddNotWrittenTag(tag); } processor.OpenNotWrittenTags(); } - private sealed class _PdfCleanUpProcessor_161 : PdfCleanUpProcessor { - public _PdfCleanUpProcessor_161(LinkedList tags, IList baseArg1, PdfDocument baseArg2 + private sealed class _PdfCleanUpProcessor_160 : PdfCleanUpProcessor { + public _PdfCleanUpProcessor_160(LinkedList tags, IList baseArg1, PdfDocument baseArg2 ) : base(baseArg1, baseArg2) { this.tags = tags; @@ -160,12 +160,12 @@ public _PdfCleanUpProcessor_161(LinkedList tags, IList bas //\cond DO_NOT_DOCUMENT internal override PdfCanvas GetCanvas() { - return new _PdfCanvas_164(tags, new PdfStream(), null, null); + return new _PdfCanvas_163(tags, new PdfStream(), null, null); } //\endcond - private sealed class _PdfCanvas_164 : PdfCanvas { - public _PdfCanvas_164(LinkedList tags, PdfStream baseArg1, PdfResources baseArg2, PdfDocument baseArg3 + private sealed class _PdfCanvas_163 : PdfCanvas { + public _PdfCanvas_163(LinkedList tags, PdfStream baseArg1, PdfResources baseArg2, PdfDocument baseArg3 ) : base(baseArg1, baseArg2, baseArg3) { this.tags = tags; diff --git a/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpToolTest.cs b/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpToolTest.cs index 95ef830..05092b7 100644 --- a/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpToolTest.cs +++ b/itext.tests/itext.cleanup.tests/itext/pdfcleanup/PdfCleanUpToolTest.cs @@ -638,8 +638,7 @@ public virtual void AutoCleanWithFalseProcessAnnotationTest() { IList additionalLocation = new List(); additionalLocation.Add(new iText.PdfCleanup.PdfCleanUpLocation(1, new Rectangle(100, 560, 200, 30))); - CleanUpProperties properties = new CleanUpProperties(); - properties.SetProcessAnnotations(false); + CleanUpProperties properties = new CleanUpProperties().SetProcessAnnotations(false); PdfCleaner.AutoSweepCleanUp(new FileStream(input, FileMode.Open, FileAccess.Read), new FileStream(output, FileMode.Create), strategy, additionalLocation, properties); CompareByContent(cmp, output, OUTPUT_PATH, "autoCleanWithFalseProcessAnnotationTest"); diff --git a/itext.tests/itext.cleanup.tests/resources/itext/pdfcleanup/BigDocumentCleanUpTest/cmp_bigTaggedDocument.pdf b/itext.tests/itext.cleanup.tests/resources/itext/pdfcleanup/BigDocumentCleanUpTest/cmp_bigTaggedDocument.pdf index 69f4132..7775979 100644 Binary files a/itext.tests/itext.cleanup.tests/resources/itext/pdfcleanup/BigDocumentCleanUpTest/cmp_bigTaggedDocument.pdf and b/itext.tests/itext.cleanup.tests/resources/itext/pdfcleanup/BigDocumentCleanUpTest/cmp_bigTaggedDocument.pdf differ diff --git a/itext/itext.cleanup/Properties/AssemblyInfo.cs b/itext/itext.cleanup/Properties/AssemblyInfo.cs index 980dbe7..5fc4cc5 100644 --- a/itext/itext.cleanup/Properties/AssemblyInfo.cs +++ b/itext/itext.cleanup/Properties/AssemblyInfo.cs @@ -21,6 +21,6 @@ [assembly: Guid("ec333396-8945-4d69-b626-475b4e2ede61")] -[assembly: AssemblyVersion("4.0.3.0")] -[assembly: AssemblyFileVersion("4.0.3.0")] -[assembly: AssemblyInformationalVersion("4.0.3")] +[assembly: AssemblyVersion("5.0.0.0")] +[assembly: AssemblyFileVersion("5.0.0.0")] +[assembly: AssemblyInformationalVersion("5.0.0")] diff --git a/itext/itext.cleanup/itext.cleanup.csproj b/itext/itext.cleanup/itext.cleanup.csproj index 52f7489..7845fd7 100644 --- a/itext/itext.cleanup/itext.cleanup.csproj +++ b/itext/itext.cleanup/itext.cleanup.csproj @@ -30,7 +30,7 @@ - + diff --git a/itext/itext.cleanup/itext/pdfcleanup/CleanUpProperties.cs b/itext/itext.cleanup/itext/pdfcleanup/CleanUpProperties.cs index b5d9bd2..0430478 100644 --- a/itext/itext.cleanup/itext/pdfcleanup/CleanUpProperties.cs +++ b/itext/itext.cleanup/itext/pdfcleanup/CleanUpProperties.cs @@ -37,6 +37,8 @@ public class CleanUpProperties { private double? overlapRatio; + private PathOffsetApproximationProperties offsetProperties = new PathOffsetApproximationProperties(); + /// Creates default CleanUpProperties instance. public CleanUpProperties() { processAnnotations = true; @@ -52,8 +54,14 @@ internal virtual IMetaInfo GetMetaInfo() { /// Sets additional meta info. /// the meta info to set - public virtual void SetMetaInfo(IMetaInfo metaInfo) { + /// + /// this + /// + /// instance + /// + public virtual iText.PdfCleanup.CleanUpProperties SetMetaInfo(IMetaInfo metaInfo) { this.metaInfo = metaInfo; + return this; } /// Check if page annotations will be processed. @@ -78,8 +86,14 @@ public virtual bool IsProcessAnnotations() { /// Default processing behaviour: remove annotation if there is overlap with a redaction region. /// /// is page annotations will be processed - public virtual void SetProcessAnnotations(bool processAnnotations) { + /// + /// this + /// + /// instance + /// + public virtual iText.PdfCleanup.CleanUpProperties SetProcessAnnotations(bool processAnnotations) { this.processAnnotations = processAnnotations; + return this; } /// Gets the overlap ratio. @@ -106,16 +120,61 @@ public virtual void SetProcessAnnotations(bool processAnnotations) { /// Example: if the overlap ratio is set to 0.3, the content region will be removed if it overlaps with /// the redaction area by at least 30%. /// - /// The overlap ratio to set. - public virtual void SetOverlapRatio(double? overlapRatio) { + /// the overlap ratio to set + /// + /// this + /// + /// instance + /// + public virtual iText.PdfCleanup.CleanUpProperties SetOverlapRatio(double? overlapRatio) { if (overlapRatio == null) { this.overlapRatio = null; - return; + return this; } if (overlapRatio <= 0 || overlapRatio > 1) { throw new ArgumentException(CleanupExceptionMessageConstant.OVERLAP_RATIO_SHOULD_BE_IN_RANGE); } this.overlapRatio = overlapRatio; + return this; + } + + /// + /// Get + /// + /// specifying approximation parameters for + /// + /// operations. + /// + /// + /// + /// + /// parameters + /// + public virtual PathOffsetApproximationProperties GetOffsetProperties() { + return offsetProperties; + } + + /// + /// Set + /// + /// specifying approximation parameters for + /// + /// operations. + /// + /// + /// + /// + /// to set + /// + /// + /// this + /// + /// instance + /// + public virtual iText.PdfCleanup.CleanUpProperties SetOffsetProperties(PathOffsetApproximationProperties offsetProperties + ) { + this.offsetProperties = offsetProperties; + return this; } } } diff --git a/itext/itext.cleanup/itext/pdfcleanup/PathOffsetApproximationProperties.cs b/itext/itext.cleanup/itext/pdfcleanup/PathOffsetApproximationProperties.cs new file mode 100644 index 0000000..397c957 --- /dev/null +++ b/itext/itext.cleanup/itext/pdfcleanup/PathOffsetApproximationProperties.cs @@ -0,0 +1,131 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +namespace iText.PdfCleanup { + /// + /// Contains properties for + /// + /// operations. + /// + public class PathOffsetApproximationProperties { + private double arcTolerance = 0.0025; + + private bool calculateOffsetMultiplierDynamically = false; + + /// + /// Creates new + /// + /// instance. + /// + public PathOffsetApproximationProperties() { + } + + // Empty constructor. + /// Specifies if floatMultiplier should be calculated dynamically. + /// + /// Specifies if floatMultiplier should be calculated dynamically. Default value is + /// . + /// + /// When a document with line arts is being cleaned up, there are a lot of calculations with floating point numbers. + /// All of them are translated into fixed point numbers by multiplying by this floatMultiplier coefficient. + /// It is possible to dynamically adjust the preciseness of the calculations. + /// + /// + /// + /// + /// if floatMultiplier should be calculated dynamically, + /// + /// for default value specified by + /// + /// + /// + /// this + /// + /// instance + /// + public virtual iText.PdfCleanup.PathOffsetApproximationProperties CalculateOffsetMultiplierDynamically(bool + calculateDynamically) { + this.calculateOffsetMultiplierDynamically = calculateDynamically; + return this; + } + + /// Checks whether floatMultiplier should be calculated dynamically. + /// + /// Checks whether floatMultiplier should be calculated dynamically. + /// + /// When a document with line arts is being cleaned up, there are a lot of calculations with floating point numbers. + /// All of them are translated into fixed point numbers by multiplying by this floatMultiplier coefficient. + /// It is possible to dynamically adjust the preciseness of the calculations. + /// + /// + /// + /// + /// if floatMultiplier should be calculated dynamically, + /// + /// for default value + /// + public virtual bool CalculateOffsetMultiplierDynamically() { + return this.calculateOffsetMultiplierDynamically; + } + + /// + /// Gets arc tolerance which is the maximum difference between the true and the faceted representation of curves + /// (arcs) in units. + /// + /// + /// Gets arc tolerance which is the maximum difference between the true and the faceted representation of curves + /// (arcs) in units. Used as the criterion of a good approximation of rounded line joins and line caps. + /// + /// Since flattened paths can never perfectly represent arcs, this field/property specifies a maximum acceptable + /// imprecision (tolerance) when arcs are approximated in an offsetting operation. Smaller values will increase + /// smoothness up to a point though at a cost of performance and in creating more vertices to construct the arc. + /// + /// arc tolerance specifying maximum difference between the true and the faceted representation of arcs + /// + public virtual double GetArcTolerance() { + return arcTolerance; + } + + /// + /// Sets arc tolerance which is the maximum difference between the true and the faceted representation of curves + /// (arcs) in units. + /// + /// + /// Sets arc tolerance which is the maximum difference between the true and the faceted representation of curves + /// (arcs) in units. Used as the criterion of a good approximation of rounded line joins and line caps. + /// + /// Since flattened paths can never perfectly represent arcs, this field/property specifies a maximum acceptable + /// imprecision (tolerance) when arcs are approximated in an offsetting operation. Smaller values will increase + /// smoothness up to a point though at a cost of performance and in creating more vertices to construct the arc. + /// + /// maximum difference between the true and the faceted representation of arcs + /// + /// this + /// + /// instance + /// + public virtual iText.PdfCleanup.PathOffsetApproximationProperties SetArcTolerance(double arcTolerance) { + this.arcTolerance = arcTolerance; + return this; + } + } +} diff --git a/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpFilter.cs b/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpFilter.cs index 7c8cbad..4b7105c 100644 --- a/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpFilter.cs +++ b/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpFilter.cs @@ -45,8 +45,8 @@ internal class PdfCleanUpFilter { private static readonly ILogger logger = ITextLogManager.GetLogger(typeof(iText.PdfCleanup.PdfCleanUpFilter )); - /* There is no exact representation of the circle using Bezier curves. - * But, for a Bezier curve with n segments the optimal distance to the control points, + /* There is no exact representation of the circle using Bézier curves. + * But, for a Bézier curve with n segments the optimal distance to the control points, * in the sense that the middle of the curve lies on the circle itself, is (4/3) * tan(pi / (2*n)) * So for 4 points it is (4/3) * tan(pi/8) = 4 * (sqrt(2)-1)/3 = 0.5522847498 * In this approximation, the Bézier curve always falls outside the circle, @@ -93,13 +93,15 @@ internal static bool ImageSupportsDirectCleanup(PdfImageXObject image) { /// true if the rectangles intersect, false otherwise internal virtual bool CheckIfRectanglesIntersect(Point[] rect1, Point[] rect2) { Clipper clipper = new Clipper(); + ClipperBridge clipperBridge = properties.GetOffsetProperties().CalculateOffsetMultiplierDynamically() ? new + ClipperBridge(rect1, rect2) : new ClipperBridge(); // If the redaction area is degenerate, the result will be false - if (!ClipperBridge.AddPolygonToClipper(clipper, rect2, PolyType.CLIP)) { + if (!clipperBridge.AddPolygonToClipper(clipper, rect2, PolyType.CLIP)) { // If the content area is not degenerate (and the redaction area is), let's return false: // even if they overlaps somehow, we do not consider it as an intersection. // If the content area is degenerate, let's process this case specifically - if (!ClipperBridge.AddPolygonToClipper(clipper, rect1, PolyType.SUBJECT)) { - // Clipper fails to process degenerate redaction areas. However that's vital for pdfAutoSweep, + if (!clipperBridge.AddPolygonToClipper(clipper, rect1, PolyType.SUBJECT)) { + // Clipper fails to process degenerate redaction areas. However, that's vital for pdfAutoSweep, // because in some cases (for example, noninvertible cm) the text's area might be degenerate, // but we still need to sweep the content. // The idea is as follows: @@ -107,7 +109,7 @@ internal virtual bool CheckIfRectanglesIntersect(Point[] rect1, Point[] rect2) { // b) if the degenerate redaction area represents a line, let's check that there the redaction line // equals to one of the edges of the content's area. That is implemented in respect to area generation, // because the redaction line corresponds to the descent line of the content. - if (!ClipperBridge.AddPolylineSubjectToClipper(clipper, rect2)) { + if (!clipperBridge.AddPolylineSubjectToClipper(clipper, rect2)) { return false; } if (rect1.Length != rect2.Length) { @@ -121,8 +123,8 @@ internal virtual bool CheckIfRectanglesIntersect(Point[] rect1, Point[] rect2) { break; } } - for (int i = 0; i < rect1.Length; i++) { - if (IsPointOnALineSegment(rect1[i], startPoint, endPoint, true)) { + foreach (Point point in rect1) { + if (IsPointOnALineSegment(point, startPoint, endPoint, true)) { return true; } } @@ -138,14 +140,14 @@ internal virtual bool CheckIfRectanglesIntersect(Point[] rect1, Point[] rect2) { // If addition returns false, this means that there are less than 3 distinct points, because of rectangle zero width. // Let's in this case specify the path as polyline, because we still want to know if redaction area // intersects even with zero-width rectangles. - bool intersectionSubjectAdded = ClipperBridge.AddPolygonToClipper(clipper, rect1, PolyType.SUBJECT); + bool intersectionSubjectAdded = clipperBridge.AddPolygonToClipper(clipper, rect1, PolyType.SUBJECT); if (intersectionSubjectAdded) { // working with paths is considered to be a bit faster in terms of performance. Paths paths = new Paths(); clipper.Execute(ClipType.INTERSECTION, paths, PolyFillType.NON_ZERO, PolyFillType.NON_ZERO); - return CheckIfIntersectionOccurs(paths, rect1, false); + return CheckIfIntersectionOccurs(paths, rect1, false, clipperBridge); } - intersectionSubjectAdded = ClipperBridge.AddPolylineSubjectToClipper(clipper, rect1); + intersectionSubjectAdded = clipperBridge.AddPolylineSubjectToClipper(clipper, rect1); if (!intersectionSubjectAdded) { // According to the comment above, // this could have happened only if all four passed points are actually the same point. @@ -156,17 +158,18 @@ internal virtual bool CheckIfRectanglesIntersect(Point[] rect1, Point[] rect2) { Array.Copy(rect1, 0, expandedRect1, 0, rect1.Length); expandedRect1[rect1.Length] = new Point(rect1[0].GetX() + SMALL_DIFF, rect1[0].GetY()); rect1 = expandedRect1; - intersectionSubjectAdded = ClipperBridge.AddPolylineSubjectToClipper(clipper, rect1); + intersectionSubjectAdded = clipperBridge.AddPolylineSubjectToClipper(clipper, rect1); System.Diagnostics.Debug.Assert(intersectionSubjectAdded); } PolyTree polyTree = new PolyTree(); clipper.Execute(ClipType.INTERSECTION, polyTree, PolyFillType.NON_ZERO, PolyFillType.NON_ZERO); return CheckIfIntersectionOccurs(iText.Kernel.Pdf.Canvas.Parser.ClipperLib.Clipper.PolyTreeToPaths(polyTree - ), rect1, true); + ), rect1, true, clipperBridge); } //\endcond - private bool CheckIfIntersectionOccurs(Paths paths, Point[] rect1, bool isDegenerate) { + private bool CheckIfIntersectionOccurs(Paths paths, Point[] rect1, bool isDegenerate, ClipperBridge clipperBridge + ) { if (paths.IsEmpty()) { return false; } @@ -174,10 +177,10 @@ private bool CheckIfIntersectionOccurs(Paths paths, Point[] rect1, bool isDegene // If the user defines a overlappingRatio we use this to calculate whether it intersects enough // To pass as an intersection if (properties.GetOverlapRatio() == null) { - return !CheckIfIntersectionRectangleDegenerate(intersectionRectangle, isDegenerate); + return !CheckIfIntersectionRectangleDegenerate(intersectionRectangle, isDegenerate, clipperBridge); } double overlappedArea = CleanUpHelperUtil.CalculatePolygonArea(rect1); - double intersectionArea = ClipperBridge.LongRectCalculateHeight(intersectionRectangle) * ClipperBridge.LongRectCalculateWidth + double intersectionArea = clipperBridge.LongRectCalculateHeight(intersectionRectangle) * clipperBridge.LongRectCalculateWidth (intersectionRectangle); double percentageOfOverlapping = intersectionArea / overlappedArea; float SMALL_VALUE_FOR_ROUNDING_ERRORS = 1e-5f; @@ -286,12 +289,10 @@ internal virtual FilteredImagesCache.FilteredImageKey CreateFilteredImageKey(Pdf /// private Path FilterFillPath(Path path, Matrix ctm, int fillingRule) { path.CloseAllSubpaths(); - Clipper clipper = new Clipper(); - ClipperBridge.AddPath(clipper, path, PolyType.SUBJECT); + IList transfRectVerticesList = new List(); foreach (Rectangle rectangle in regions) { try { - Point[] transfRectVertices = TransformPoints(ctm, true, GetRectangleVertices(rectangle)); - ClipperBridge.AddPolygonToClipper(clipper, transfRectVertices, PolyType.CLIP); + transfRectVerticesList.Add(TransformPoints(ctm, true, GetRectangleVertices(rectangle))); } catch (PdfException e) { if (!(e.InnerException is NoninvertibleTransformException)) { @@ -303,13 +304,20 @@ private Path FilterFillPath(Path path, Matrix ctm, int fillingRule) { } } } + Clipper clipper = new Clipper(); + ClipperBridge clipperBridge = properties.GetOffsetProperties().CalculateOffsetMultiplierDynamically() ? GetClipperBridge + (path, transfRectVerticesList) : new ClipperBridge(); + clipperBridge.AddPath(clipper, path, PolyType.SUBJECT); + foreach (Point[] transfRectVertices in transfRectVerticesList) { + clipperBridge.AddPolygonToClipper(clipper, transfRectVertices, PolyType.CLIP); + } PolyFillType fillType = PolyFillType.NON_ZERO; if (fillingRule == PdfCanvasConstants.FillingRule.EVEN_ODD) { fillType = PolyFillType.EVEN_ODD; } PolyTree resultTree = new PolyTree(); clipper.Execute(ClipType.DIFFERENCE, resultTree, fillType, PolyFillType.NON_ZERO); - return ClipperBridge.ConvertToPath(resultTree); + return clipperBridge.ConvertToPath(resultTree); } /// Calculates intersection of the image and the render filter region in the coordinate system relative to the image. @@ -350,13 +358,15 @@ private Path FilterStrokePath(Path sourcePath, Matrix ctm, float lineWidth, int if (lineDashPattern != null && !lineDashPattern.IsSolid()) { path = LineDashPattern.ApplyDashPattern(path, lineDashPattern); } - ClipperOffset offset = new ClipperOffset(miterLimit, iText.PdfCleanup.PdfCleanUpTool.arcTolerance * iText.PdfCleanup.PdfCleanUpTool - .floatMultiplier); - IList degenerateSubpaths = ClipperBridge.AddPath(offset, path, joinType, endType); + ClipperBridge clipperBridge = properties.GetOffsetProperties().CalculateOffsetMultiplierDynamically() ? new + ClipperBridge(path) : new ClipperBridge(); + ClipperOffset offset = new ClipperOffset(miterLimit, properties.GetOffsetProperties().GetArcTolerance() * + clipperBridge.GetFloatMultiplier()); + IList degenerateSubpaths = clipperBridge.AddPath(offset, path, joinType, endType); PolyTree resultTree = new PolyTree(); - offset.Execute(resultTree, lineWidth * iText.PdfCleanup.PdfCleanUpTool.floatMultiplier / 2); - Path offsetedPath = ClipperBridge.ConvertToPath(resultTree); - if (degenerateSubpaths.Count > 0) { + offset.Execute(resultTree, lineWidth * clipperBridge.GetFloatMultiplier() / 2); + Path offsetedPath = clipperBridge.ConvertToPath(resultTree); + if (!degenerateSubpaths.IsEmpty()) { if (endType == EndType.OPEN_ROUND) { IList circles = ConvertToCircles(degenerateSubpaths, lineWidth / 2); offsetedPath.AddSubpaths(circles); @@ -422,7 +432,7 @@ private static PdfCleanUpFilter.FilterResult FilterImage(PdfImageXObj /// Checks if the input intersection rectangle is degenerate. /// In case of intersection subject is degenerate (isIntersectSubjectDegenerate /// is true) and it is included into intersecting rectangle, this method returns false, - /// despite of the intersection rectangle is degenerate. + /// despite the intersection rectangle is degenerate. /// /// intersection rectangle /// @@ -431,29 +441,29 @@ private static PdfCleanUpFilter.FilterResult FilterImage(PdfImageXObj /// /// true - if the intersection rectangle is degenerate. private static bool CheckIfIntersectionRectangleDegenerate(IntRect rect, bool isIntersectSubjectDegenerate - ) { - float width = ClipperBridge.LongRectCalculateWidth(rect); - float height = ClipperBridge.LongRectCalculateHeight(rect); + , ClipperBridge clipperBridge) { + float width = clipperBridge.LongRectCalculateWidth(rect); + float height = clipperBridge.LongRectCalculateHeight(rect); return isIntersectSubjectDegenerate ? (width < EPS && height < EPS) : (width < EPS || height < EPS); } private static bool IsPointOnALineSegment(Point currPoint, Point linePoint1, Point linePoint2, bool isBetweenLinePoints ) { - double dxc = currPoint.x - linePoint1.x; - double dyc = currPoint.y - linePoint1.y; - double dxl = linePoint2.x - linePoint1.x; - double dyl = linePoint2.y - linePoint1.y; + double dxc = currPoint.GetX() - linePoint1.GetX(); + double dyc = currPoint.GetY() - linePoint1.GetY(); + double dxl = linePoint2.GetX() - linePoint1.GetX(); + double dyl = linePoint2.GetY() - linePoint1.GetY(); double cross = dxc * dyl - dyc * dxl; // if point is on a line, let's check whether it's between provided line points if (Math.Abs(cross) <= EPS) { if (isBetweenLinePoints) { if (Math.Abs(dxl) >= Math.Abs(dyl)) { - return dxl > 0 ? linePoint1.x - EPS <= currPoint.x && currPoint.x <= linePoint2.x + EPS : linePoint2.x - EPS - <= currPoint.x && currPoint.x <= linePoint1.x + EPS; + return dxl > 0 ? linePoint1.GetX() - EPS <= currPoint.GetX() && currPoint.GetX() <= linePoint2.GetX() + EPS + : linePoint2.GetX() - EPS <= currPoint.GetX() && currPoint.GetX() <= linePoint1.GetX() + EPS; } else { - return dyl > 0 ? linePoint1.y - EPS <= currPoint.y && currPoint.y <= linePoint2.y + EPS : linePoint2.y - EPS - <= currPoint.y && currPoint.y <= linePoint1.y + EPS; + return dyl > 0 ? linePoint1.GetY() - EPS <= currPoint.GetY() && currPoint.GetY() <= linePoint2.GetY() + EPS + : linePoint2.GetY() - EPS <= currPoint.GetY() && currPoint.GetY() <= linePoint1.GetY() + EPS; } } else { @@ -488,8 +498,8 @@ private static Rectangle CalcImageRect(Matrix imageCtm) { if (imageCtm == null) { return null; } - Point[] points = TransformPoints(imageCtm, false, new Point(0, 0), new Point(0, 1), new Point(1, 0), new Point - (1, 1)); + Point[] points = TransformPoints(imageCtm, false, new Point(0d, 0d), new Point(0d, 1d), new Point(1d, 0d), + new Point(1d, 1d)); return Rectangle.CalculateBBox(JavaUtil.ArraysAsList(points)); } @@ -718,9 +728,8 @@ private static Point[] GetTextRectangle(TextRenderInfo renderInfo) { /// Convert a Rectangle object into 4 Points /// input Rectangle private static Point[] GetRectangleVertices(Rectangle rect) { - Point[] points = new Point[] { new Point(rect.GetLeft(), rect.GetBottom()), new Point(rect.GetRight(), rect - .GetBottom()), new Point(rect.GetRight(), rect.GetTop()), new Point(rect.GetLeft(), rect.GetTop()) }; - return points; + return new Point[] { new Point(rect.GetLeft(), rect.GetBottom()), new Point(rect.GetRight(), rect.GetBottom + ()), new Point(rect.GetRight(), rect.GetTop()), new Point(rect.GetLeft(), rect.GetTop()) }; } /// Calculate the intersection of 2 Rectangles. @@ -734,12 +743,25 @@ private static Rectangle GetRectanglesIntersection(Rectangle rect1, Rectangle re return (x2 - x1 > 0 && y2 - y1 > 0) ? new Rectangle(x1, y1, x2 - x1, y2 - y1) : null; } + private static ClipperBridge GetClipperBridge(Path path, IList transfRectVerticesList) { + IList pointsList = new List(); + foreach (Subpath subpath in path.GetSubpaths()) { + if (!subpath.IsSinglePointClosed() && !subpath.IsSinglePointOpen()) { + pointsList.AddAll(subpath.GetPiecewiseLinearApproximation()); + } + } + foreach (Point[] transfRectVertices in transfRectVerticesList) { + pointsList.AddAll(JavaUtil.ArraysAsList(transfRectVertices)); + } + return new ClipperBridge(pointsList.ToArray(new Point[0])); + } + //\cond DO_NOT_DOCUMENT /// Generic class representing the result of filtering an object of type T. internal class FilterResult { - private bool isModified; + private readonly bool isModified; - private T filterResult; + private readonly T filterResult; public FilterResult(bool isModified, T filterResult) { this.isModified = isModified; diff --git a/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpProcessor.cs b/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpProcessor.cs index 680c989..42a7c82 100644 --- a/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpProcessor.cs +++ b/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpProcessor.cs @@ -37,7 +37,7 @@ You should have received a copy of the GNU Affero General Public License using iText.Kernel.Pdf.Canvas.Parser; using iText.Kernel.Pdf.Canvas.Parser.Data; using iText.Kernel.Pdf.Canvas.Parser.Listener; -using iText.Kernel.Pdf.Colorspace; +using iText.Kernel.Pdf.Colorspace.Shading; using iText.Kernel.Pdf.Tagutils; using iText.Kernel.Pdf.Xobject; using iText.PdfCleanup.Logs; @@ -544,7 +544,7 @@ private void FilterContent(String @operator, IList operands) { } else { if ("sh".Equals(@operator)) { - PdfShading shading = GetResources().GetShading((PdfName)operands[0]); + AbstractPdfShading shading = GetResources().GetShading((PdfName)operands[0]); GetCanvas().PaintShading(shading); } else { diff --git a/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpTool.cs b/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpTool.cs index 02ec015..d44594d 100644 --- a/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpTool.cs +++ b/itext/itext.cleanup/itext/pdfcleanup/PdfCleanUpTool.cs @@ -43,36 +43,14 @@ You should have received a copy of the GNU Affero General Public License namespace iText.PdfCleanup { /// Represents the main mechanism for cleaning a PDF document. public class PdfCleanUpTool { - /// - /// When a document with line arts is being cleaned up, there are a lot of - /// calculations with floating point numbers. - /// - /// - /// When a document with line arts is being cleaned up, there are a lot of - /// calculations with floating point numbers. All of them are translated - /// into fixed point numbers by multiplying by this coefficient. Vary it - /// to adjust the preciseness of the calculations. - /// - [System.ObsoleteAttribute] - public static double floatMultiplier = Math - //TODO DEVSIX-5770 make this constant a single non-static configuration - .Pow(10, 14); - - /// - /// Used as the criterion of a good approximation of rounded line joins - /// and line caps. - /// - [System.ObsoleteAttribute] - public static double arcTolerance = - //TODO DEVSIX-5770 make this constant a single non-static configuration - 0.0025; - - private PdfDocument pdfDocument; + private readonly PdfDocument pdfDocument; - private CleanUpProperties properties; + private readonly CleanUpProperties properties; /// Key - page number, value - list of locations related to the page. - private IDictionary> pdfCleanUpLocations; + private readonly IDictionary> pdfCleanUpLocations; + + private readonly FilteredImagesCache filteredImagesCache; /// /// Keys - redact annotations to be removed from the document after clean up, @@ -80,8 +58,6 @@ public class PdfCleanUpTool { /// private IDictionary> redactAnnotations; - private FilteredImagesCache filteredImagesCache; - /// /// Creates a /// diff --git a/itext/itext.cleanup/itext/pdfcleanup/actions/data/PdfSweepProductData.cs b/itext/itext.cleanup/itext/pdfcleanup/actions/data/PdfSweepProductData.cs index bab2682..fa5034e 100644 --- a/itext/itext.cleanup/itext/pdfcleanup/actions/data/PdfSweepProductData.cs +++ b/itext/itext.cleanup/itext/pdfcleanup/actions/data/PdfSweepProductData.cs @@ -34,7 +34,7 @@ public class PdfSweepProductData { public const String PDF_SWEEP_PUBLIC_PRODUCT_NAME = PDF_SWEEP_PRODUCT_NAME; - private const String PDF_SWEEP_VERSION = "4.0.3"; + private const String PDF_SWEEP_VERSION = "5.0.0"; private const int PDF_SWEEP_COPYRIGHT_SINCE = 2000; diff --git a/itext/itext.cleanup/itext/pdfcleanup/util/CleanUpHelperUtil.cs b/itext/itext.cleanup/itext/pdfcleanup/util/CleanUpHelperUtil.cs index b78c1dd..b2f3a37 100644 --- a/itext/itext.cleanup/itext/pdfcleanup/util/CleanUpHelperUtil.cs +++ b/itext/itext.cleanup/itext/pdfcleanup/util/CleanUpHelperUtil.cs @@ -63,14 +63,14 @@ public static double CalculatePolygonArea(Point[] vertices) { double sum = 0; for (int i = 0; i < vertices.Length; i++) { if (i == 0) { - sum += vertices[i].x * (vertices[i + 1].y - vertices[vertices.Length - 1].y); + sum += vertices[i].GetX() * (vertices[i + 1].GetY() - vertices[vertices.Length - 1].GetY()); } else { if (i == vertices.Length - 1) { - sum += vertices[i].x * (vertices[0].y - vertices[i - 1].y); + sum += vertices[i].GetX() * (vertices[0].GetY() - vertices[i - 1].GetY()); } else { - sum += vertices[i].x * (vertices[i + 1].y - vertices[i - 1].y); + sum += vertices[i].GetX() * (vertices[i + 1].GetY() - vertices[i - 1].GetY()); } } } diff --git a/pdfSweep.nuspec b/pdfSweep.nuspec index a343f6b..fe41452 100644 --- a/pdfSweep.nuspec +++ b/pdfSweep.nuspec @@ -2,7 +2,7 @@ itext.pdfsweep - 4.0.3 + 5.0.0 iText pdfSweep iText Software iText Software @@ -18,7 +18,7 @@ itext itext7 itextsharp c# .net csharp pdf cleanup pdfsweep - + diff --git a/port-hash b/port-hash index 643509c..22a220e 100644 --- a/port-hash +++ b/port-hash @@ -1 +1 @@ -0e953a50b766ff85fd7a59dd2b918ccd8868b898 +e0e4a8d90a3f73ebc56a3808dcf7b524a293b0e0