From cdf720ecef114810bf643b51116a3a9bbfc29d9b Mon Sep 17 00:00:00 2001 From: Alexey Subach Date: Wed, 29 Nov 2023 15:05:01 +0000 Subject: [PATCH 1/4] Support stroke-dasharray and stroke-dashoffset css attributes For now only absolute values are supported. Percents are not supported. DEVSIX-4043 Autoported commit. Original commit hash: [4f2b6a09d] --- .../SvgStrokeParameterConverterUnitTest.cs | 79 ++++++++++ .../itext/svg/renderers/StrokeTest.cs | 5 + .../cmp_usingJFreeSvgBarChartFromFileTest.pdf | Bin 57069 -> 57195 bytes ...mp_usingJFreeSvgBarChartFromStringTest.pdf | Bin 57069 -> 57195 bytes ...cmp_usingJFreeSvgLineChartFromFileTest.pdf | Bin 13520 -> 13646 bytes ...p_usingJFreeSvgLineChartFromStringTest.pdf | Bin 13520 -> 13646 bytes .../cmp_usingJFreeSvgXYChartFromFileTest.pdf | Bin 11239 -> 11464 bytes ...cmp_usingJFreeSvgXYChartFromStringTest.pdf | Bin 11239 -> 11464 bytes .../impl/StrokeTest/cmp_strokeAdvanced.pdf | Bin 2410 -> 2639 bytes .../impl/StrokeTest/cmp_strokeWithDashes.pdf | Bin 0 -> 2021 bytes .../impl/StrokeTest/strokeWithDashes.svg | 64 ++++++++ .../svg/css/SvgStrokeParameterConverter.cs | 144 ++++++++++++++++++ .../itext/svg/logs/SvgLogMessageConstant.cs | 3 + .../renderers/impl/AbstractSvgNodeRenderer.cs | 73 +++++---- port-hash | 2 +- 15 files changed, 338 insertions(+), 32 deletions(-) create mode 100644 itext.tests/itext.svg.tests/itext/svg/css/SvgStrokeParameterConverterUnitTest.cs create mode 100644 itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/StrokeTest/cmp_strokeWithDashes.pdf create mode 100644 itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/StrokeTest/strokeWithDashes.svg create mode 100644 itext/itext.svg/itext/svg/css/SvgStrokeParameterConverter.cs diff --git a/itext.tests/itext.svg.tests/itext/svg/css/SvgStrokeParameterConverterUnitTest.cs b/itext.tests/itext.svg.tests/itext/svg/css/SvgStrokeParameterConverterUnitTest.cs new file mode 100644 index 0000000000..9658f9de96 --- /dev/null +++ b/itext.tests/itext.svg.tests/itext/svg/css/SvgStrokeParameterConverterUnitTest.cs @@ -0,0 +1,79 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2023 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 iText.Svg.Logs; +using iText.Test; +using iText.Test.Attributes; + +namespace iText.Svg.Css { + [NUnit.Framework.Category("UnitTest")] + public class SvgStrokeParameterConverterUnitTest : ExtendedITextTest { + [LogMessage(SvgLogMessageConstant.PERCENTAGE_VALUES_IN_STROKE_DASHARRAY_AND_STROKE_DASHOFFSET_ARE_NOT_SUPPORTED + )] + [NUnit.Framework.Test] + public virtual void TestStrokeDashArrayPercentsAreNotSupported() { + NUnit.Framework.Assert.IsNull(SvgStrokeParameterConverter.ConvertStrokeDashParameters("5,3%", null)); + } + + [NUnit.Framework.Test] + public virtual void TestStrokeDashArrayOddNumberOfValues() { + SvgStrokeParameterConverter.PdfLineDashParameters result = SvgStrokeParameterConverter.ConvertStrokeDashParameters + ("5pt", null); + NUnit.Framework.Assert.IsNotNull(result); + NUnit.Framework.Assert.AreEqual(0, result.GetDashPhase(), 0); + iText.Test.TestUtil.AreEqual(new float[] { 5, 5 }, result.GetDashArray(), 1e-5f); + } + + [NUnit.Framework.Test] + public virtual void TestEmptyStrokeDashArray() { + SvgStrokeParameterConverter.PdfLineDashParameters result = SvgStrokeParameterConverter.ConvertStrokeDashParameters + ("", null); + NUnit.Framework.Assert.IsNull(result); + } + + [LogMessage(SvgLogMessageConstant.PERCENTAGE_VALUES_IN_STROKE_DASHARRAY_AND_STROKE_DASHOFFSET_ARE_NOT_SUPPORTED + )] + [NUnit.Framework.Test] + public virtual void TestStrokeDashOffsetPercentsAreNotSupported() { + SvgStrokeParameterConverter.PdfLineDashParameters result = SvgStrokeParameterConverter.ConvertStrokeDashParameters + ("5pt,3pt", "10%"); + NUnit.Framework.Assert.AreEqual(new SvgStrokeParameterConverter.PdfLineDashParameters(new float[] { 5, 3 } + , 0), result); + } + + [NUnit.Framework.Test] + public virtual void TestEmptyStrokeDashOffset() { + SvgStrokeParameterConverter.PdfLineDashParameters result = SvgStrokeParameterConverter.ConvertStrokeDashParameters + ("5pt,3pt", ""); + NUnit.Framework.Assert.AreEqual(new SvgStrokeParameterConverter.PdfLineDashParameters(new float[] { 5, 3 } + , 0), result); + } + + [NUnit.Framework.Test] + public virtual void TestStrokeDashOffset() { + SvgStrokeParameterConverter.PdfLineDashParameters result = SvgStrokeParameterConverter.ConvertStrokeDashParameters + ("5pt,3pt", "10"); + NUnit.Framework.Assert.AreEqual(new SvgStrokeParameterConverter.PdfLineDashParameters(new float[] { 5, 3 } + , 7.5f), result); + } + } +} diff --git a/itext.tests/itext.svg.tests/itext/svg/renderers/StrokeTest.cs b/itext.tests/itext.svg.tests/itext/svg/renderers/StrokeTest.cs index ba8a188136..8b8370d508 100644 --- a/itext.tests/itext.svg.tests/itext/svg/renderers/StrokeTest.cs +++ b/itext.tests/itext.svg.tests/itext/svg/renderers/StrokeTest.cs @@ -53,6 +53,11 @@ public virtual void NoLineStrokeWidthTest() { ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "noLineStrokeWidth"); } + [NUnit.Framework.Test] + public virtual void StrokeWithDashesTest() { + ConvertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "strokeWithDashes"); + } + [NUnit.Framework.Test] public virtual void AdvancedStrokeTest() { //TODO: update cmp-file after DEVSIX-2258 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 a8a1031784afdbfc464005292a59339b6506f8ec..5fa8bb839d28ea483026572f2604330b8cd07d36 100644 GIT binary patch delta 6542 zcmai2O^aPc5altr84;BM7a@TcaWWB&c6U{EcWDrmC@N@#hzcTR;>@f>$rrft{sy@l z7b5-xm4G|lxy&Lc_yOh*hztLL=e_&hynFi$_`*Oa`ri6DRi{qhe}3Hh`=_n%&di=p zp~e(bQmIb`*$ZLs$!FjA^x)F|t;6$|o=YK(4)m|h9-nFd@$89Ny7R^Cui2K>aOe8g z=Vu>!KR#n~{CzMF^Mn5C?wS6<-FL`(n=EJge|O(I!FqU(t*86n&%H}$Pxr6ywbpO; zt~ci%Ie)XUT-`l==j-#|e%0Up`G?NtVey-PGT&bQCrQYaY*7;1Jd-WuKsHIrwz~4s zjU2176N~HwrKcBWWhW(YBBe}ybRi_E!cH7DX)EKIoM=eoM1xPAz=uX7ABqm^S|^$+ zI}v3k8rg{!c4A72Xbx*1-7)T(8Fwv=yB5Y>E90)txT~*mU&G#oWPx$F$hcc%+$}Ng zCXBmDgF7HG1?<2dzjZV)8sl!pxLao2Ei>*`7@9uQDFw(g)(%Bj5?2L2? zJm4iV&?PQ`Ui8GKBT8bVOJbypFw#XgNUwn|%0L$z13mO)D*1#LmP}J$Dg#)YJEDe4 zWM8Al7OdJOC%UO1H9BaKZCR)zD#}35R^?}X=qBX9DZht85&8*C;HlCJT^VCHo*&O? z>|qchJMT#qE7TB z7CAdhaa6*=iLqD}=!BNO#HOn1)G|WI1j5t$M}OVvtn%!e7do8-?Mcaz_O5_rDRk^- zqK&M}noZj?8_z|~nOYVY&|KuoKtuIVM-;H#rkhsniIq!zjM1k|3QJ%q__ulC09H|2 zhEmKZkJGHR<18&RkR&{0!h4OZI7HDKE492yt4$)svv z%?lYO6naF|?F%D*WJ{!n7IZ#ppJ7rpN})_5NSR)Us}b36 zm@LUoC6X|}UQ&W&p%z+51gVe>E0Jv<5gX%@Om`hcdvJh!4NK!?PSg} zRuef<5-FOE2RCP0IN&J~DGK2-4zU05&j&g*_6U-p2hj=Ra|@$Fx6pi-6Q~rrg%XBt zp14Ha_P8R9h zyo@|gD12Fe(1%dQKHMsi>hX6FTxu9&Ty)xI#pg?sKo%k?c?;<=8e@E9zfRW_tP~g`|;X=>tV(|DEh+1|2xrtd0)@!g)48q_|}zIFTcHg^Va^2Tc6!H SJleJZJ+QsI`|{;iw*Lct+5S-g delta 6445 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 TV{vU4AB<&pfB)snuk8K@M1KB5 delta 6445 zcmai2O^aMb6r~w(G24JaqahI|A84?u?nl)X6_hBt&fAR7nKmKm<=VDJyA3xap_VV)4 zl^2%J9elR>{QlsJ)#rsPVO=Ovd*Y5ckL z<)!`AAEijO<&$538@&IIq?+hi7UgHr=vi@!BvLqr#5%^X-I4$-z0fiTvBp7MIY`Y6 zq}F+iA*CWsI7lQ85>7!h2?fy<^B{OW{LErd7gA*s-M!3 z5qe)+m(Q>>aaySL3*(H|kL>iqC`@ArH%w^EVvN;Q!Wl_`TiQ%3m7az4n~{_Vh@@=& zcLmiv^l~goDlde5c7Y`sbsGjV6}Z4#a15mD^nKgu3Qd&0N+f5c&E!IbD_NYs3rEv$ zI$Kvq$iL)D>6hFxr8Nh2hm=G*BqdYpOM#g8n*rs*n{xt$=q#Idof60;4Tlj%&_R z?;dRnWIbJ_CKiy&j#w)Cv*);q5KWk*1f&|b;!r&ilIlT)RL|&=+L)3vX05cOL<#h5 z1TyA-V{QbtkB~IUgrq4eX46huAySPGLYRqlhmzC&lZG_(X>E8TJ;O}xpWz%tltiZK zDN$G*V_;_lt3d`tLbZ zQi70(a8-+vR4c|^FI+0lHG@O;e1ph84%uhyFu3gm{n@7<%mzNALd> zri6+{b!!;bV^$TzoHjF)RuBSkFe+5kx*ho&|EG{|T4;&JF_eNiw!*@NOAgwgA%*iz z-+=Te3xoAO9Z1aU^3fmX29_#oG=SBDNp&P-9zNlsi7brb5J_8ytvMxsS25tRsO%iH zQV^qYr0!&4aKSb8#bzkr*<9>bis5a?!<=3apeyL$ru7Uhg{|pGNEmKPh$|Yh2ZHut z#i(pQQFUa-#!95j)XI&ED+FE_lZfvqM#CWNci8x9bG+H+b%i;`r2wzfu5m}jK>yGV zaluIa)7Ylskig>H+MMIDaa)iH)JV0GV!$*IcTt$*fJpY)!i@)sco;W$c%A@n6(jfv zs=+oD!-&isu35%ETguOfrX)wp^=e^iKVuIvAThQx~9Hweu_(CNvej6m63rFLcwGYMrB5m z$vKQFj7FO$GTvomHnKF`T*&-Sl{eZ@&r|`3V-*Y(QYJspl%3qI8HOQRiXkF9xlk(t zi|A9VqLJE|X39)Hr5%oDf`U#ln#co9naS&P(A5D&H!J8$vpX6Zrlgu0B$=8fnx`dN vnphZ{Sel!pB%3FvB$*qVnA_PH5Ku9>#>5yHG#gCBxQxsVxKveL{oS|#FM(hZ delta 233 zcmX??bs=+uETguep`n3=p}C2Lp{2HgvAThQx~9Hweu_(CNvej6m63rFLcwGYMrB6B z$vKQFjE0*hGTvomHZ-%`T*&-Sbuy#2%;cGxVPINPD*{Z<2Gb>A_Iz!Sob+Tlop3Nc z4@%4Fg87^0=}NOZCZ(iWm?WhoC#EH*8m6WtCnp*kB%7Kir5PI=0Vz8h0|F{0|1vQK UI#AD4jLXo(giBS`)!&T^013lFM*si- diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgLineChartFromStringTest.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/JFreeSvgTest/cmp_usingJFreeSvgLineChartFromStringTest.pdf index 7a4db32aea40884241dc6c1d97f612fc71f66fb6..ca6fa935c3c9acc92a3fe166a8b9961819123505 100644 GIT binary patch delta 357 zcmcbRc`j>%ETfK*frX)wp^=e^iHWv>vAThQx~9Hweu_(CNvej6m63rFl7h*ej7p3q zlXDnV7>zbhWW3ABY-DM=xsdsvDsQx*o~Z&5$0`^oq)dLGDLc7aGYmtt6hlOIa-miP z7SX3zMI*H_&6JsZN;@3Q1O=U9G?53IGLzTopsNFlZdTBhVt2BzNKP|0GB-{$Ft-G< ul1!6Q%*;(pQ%#H#%?vFJEbVLvshC`2VhjwM4JKk-M&<@ws;aL3Zd?G#DPSf5 delta 235 zcmX??bs=+uETfL0p`n3=p}C2Lp_#UUvAThQx~9Hweu_(CNvej6m63rFl7h*ej7p4# zlXDnV7!5a1WW3ABY-nb=xsdsv>SRW3naMLX!@#tpRs@)y4W>)L?D^UtIqAuAI^ke? z9+Z~T1@kx0)0JX(N-|1GG_XuIvrJ7)wKOy`G_W)zbhWW3GDYG`0+XuP?AIZlQ*+EC9_0f=K23=~o(8!AdpekLD>MKneMQ&ej5 zWrcVQwUrnml9LZ9Mqv>(QA)rfx(2JLiZV8}b1+07s>n{3Pzggb7bM!H5`iIFiXkF9 zxkwdLoy_E?s^J)FWhRHKVG-SYQmuo<$v7=B&BD^qDB0N9B+0_UFeNoLHQ6%RG|j@? zBFQYp($0pEipi6;jTKFFgZ&%>f<630xQa^>i%KerQq#B$4GkR`8<4sETfL0p`n3=p}C2Lp_#UUvAThQx~9Hweu_(CNvej6m63rFl7h*ej7p4# zlXDnV7!5a1WW3GDVrgMvxw()zPG)kVg2d#n@^L`A49xzkkN~Ee6%!_ZR4kfYs8j)@ zB_|6g$AiVEDnsO@RUq_2l_;>dqG}A7UJRvG)gb)kY7lvKbuhhoxq3H?lVP%PN|JeE zN}@qB(50!VX{kwuiDo7S#z`rO7RIKgb~c1mOfJzeRy5QN_Hzsf_V5qkDlSPZDyb++ SP2&OvizbhWW3GDYG`0+XuP?AIZlQ*+EC9_0f=K23=~o(8!AdpekLD>MKneMQ&ej5 zWrcVQwUrnml9LZ9Mqv>(QA)rfx(2JLiZV8}b1+07s>n{3Pzggb7bM!H5`iIFiXkF9 zxkwdLoy_E?s^J)FWhRHKVG-SYQmuof<630xQa^>i%KerQq#B$4GkR`8<4sETfL0p`n3=p}C2Lk%6{>vAThQx~9Hweu_(CNvej6m63rFl7h*ej7p4# zlXDnV7!5a1WW3GDVrgMvxw()zPG)kVg2d#n@^L`A49xzkkN~Ee6%!_ZR4kfYs8j)@ zB_|6g$AiVEDnsO@RUq_2l_;>dqG}A7UJRvG)gb)kY7lvKbuhhoxq3H?Q;NA+nyF!$ zi5bw9MrJ97W`@ZImX=9|$%ba8NhSs7lbU6?ovW^wKF68L4-Z+g?oelg@RQG*7D!uX6b0%sM&g4_|V3`Q>v4 zQr@7CJQ`f>Z9b>jB= TI_cjev2_uY@0QEEtv&Y(WuS^( delta 276 zcmX>v@=9pJ<;nGox)U$U>KGat7+M$_ni?A#Ya1A=8yKi->ig!WxFnXOYPeV#85kic zm@LC6$7-%;q-QwUo>4{6P$4rUwW379y(qu5K*29ebMqobDMlt^i_QC(T9`MdvAtoO zT*Vh;sLv%4C17m~94>_bA6HN?Ll1-D- zEG<*hQcX>hQ!LEPEDTJIP0b9G6HUzQY*GoRnEaPh59$L0E-_A1b1qd?SARDy0O=J* A1poj5 diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/StrokeTest/cmp_strokeWithDashes.pdf b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/StrokeTest/cmp_strokeWithDashes.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a8a488b97c974f76fec8055d96c643fa07f54a79 GIT binary patch literal 2021 zcmc&#%Wm5^6y5VHxG|bwV2PyoQV|$P;|HPCH z22E>CvOu3uc)Xx^Sff7>GF9HGL2@p9<9bwAt*P1uJGe>s#^h!4qP{sNFch)CnzPGr zoLI2I%$rP1Q!j018d&(lY5LJ*El6%P>E;LUglD!~JU}`7a%BK&W(EUC$q|Xbql@7P zc(-L;4U^XF4WBc@B1)O2f+)&=BhfQLp6!Qk>U_7s*w*Rp8kG%hA;LGN8{J{+Smpbyx7NoGeSt;Er}1W?3c#ik*$|k`0cy>4OhD1>Vjyx zboi)_k~W#wvyHJ!)jF_x3Av-;t1@rSUFIj3;cY6Kjf=}(=knq~?z##K8U8r^%3=HT zPnT)yl6_p;#cHjtP`CZzf+4u?eo$eYEl>|P*E4MEx`pOhO|QN%x2u=js!!3DLfzs0 z@}?b4TPQR%&ZwbS1IEuX-)c;34SJ6%;OmqCF+wCz0)K{J=4E9N$X>BPRI#8N%+VA! z#V2&moJs}a5?u2g5G-H~awlZd74dJ8=b;w@r zSM01eR5IU5506*Z&g;#8=z`m9@*8UkXtfx@fzv%$3IP!mc&&>x9Eu#KrAwEAaMHX|)GnFxw7o;d+ zZA`=r*D{i+%G1Ia!}FA-aU5k_$4rt`a}lRWEUBi6%&3ZrSP;!JR-_prI?ZVUrc9GK ziWp0DqSGYjQW;K(lDTF@9Ls{IY0d`!KZDEgq$+Cc?y*TYfx{EKLvs>N%fAecq3%6@ c5B1$O)W3TTp{7l2>l=b{B~h>U^7I&e2W&L?b^rhX literal 0 HcmV?d00001 diff --git a/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/StrokeTest/strokeWithDashes.svg b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/StrokeTest/strokeWithDashes.svg new file mode 100644 index 0000000000..488d0dac4a --- /dev/null +++ b/itext.tests/itext.svg.tests/resources/itext/svg/renderers/impl/StrokeTest/strokeWithDashes.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/itext/itext.svg/itext/svg/css/SvgStrokeParameterConverter.cs b/itext/itext.svg/itext/svg/css/SvgStrokeParameterConverter.cs new file mode 100644 index 0000000000..1f28c4d441 --- /dev/null +++ b/itext/itext.svg/itext/svg/css/SvgStrokeParameterConverter.cs @@ -0,0 +1,144 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2023 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 System.Collections.Generic; +using Microsoft.Extensions.Logging; +using iText.Commons; +using iText.Commons.Utils; +using iText.StyledXmlParser.Css.Util; +using iText.Svg; +using iText.Svg.Logs; +using iText.Svg.Utils; + +namespace iText.Svg.Css { + /// This class converts stroke related SVG parameters and attributes into those from PDF specification. + /// + public sealed class SvgStrokeParameterConverter { + private SvgStrokeParameterConverter() { + } + + private static readonly ILogger LOGGER = ITextLogManager.GetLogger(typeof(iText.Svg.Css.SvgStrokeParameterConverter + )); + + /// Convert stroke related SVG parameters and attributes into PDF line dash parameters. + /// 'stroke-dasharray' css property value. + /// 'stroke-dashoffset' css property value. + /// + /// PDF line dash parameters represented by + /// . + /// + public static SvgStrokeParameterConverter.PdfLineDashParameters ConvertStrokeDashParameters(String strokeDashArray + , String strokeDashOffset) { + if (strokeDashArray != null && !SvgConstants.Values.NONE.EqualsIgnoreCase(strokeDashArray)) { + IList dashArray = SvgCssUtils.SplitValueList(strokeDashArray); + foreach (String dashArrayItem in dashArray) { + if (CssTypesValidationUtils.IsPercentageValue(dashArrayItem)) { + LOGGER.LogError(SvgLogMessageConstant.PERCENTAGE_VALUES_IN_STROKE_DASHARRAY_AND_STROKE_DASHOFFSET_ARE_NOT_SUPPORTED + ); + return null; + } + } + if (dashArray.Count > 0) { + if (dashArray.Count % 2 == 1) { + // If an odd number of values is provided, then the list of values is repeated to yield an even + // number of values. Thus, 5,3,2 is equivalent to 5,3,2,5,3,2. + dashArray.AddAll(new List(dashArray)); + } + float[] dashArrayFloat = new float[dashArray.Count]; + for (int i = 0; i < dashArray.Count; i++) { + dashArrayFloat[i] = CssDimensionParsingUtils.ParseAbsoluteLength(dashArray[i]); + } + // Parse stroke dash offset + float dashPhase = 0; + if (strokeDashOffset != null && !String.IsNullOrEmpty(strokeDashOffset) && !SvgConstants.Values.NONE.EqualsIgnoreCase + (strokeDashOffset)) { + if (CssTypesValidationUtils.IsPercentageValue(strokeDashOffset)) { + LOGGER.LogError(SvgLogMessageConstant.PERCENTAGE_VALUES_IN_STROKE_DASHARRAY_AND_STROKE_DASHOFFSET_ARE_NOT_SUPPORTED + ); + } + else { + dashPhase = CssDimensionParsingUtils.ParseAbsoluteLength(strokeDashOffset); + } + } + return new SvgStrokeParameterConverter.PdfLineDashParameters(dashArrayFloat, dashPhase); + } + } + return null; + } + + /// This class represents PDF dash parameters. + public class PdfLineDashParameters { + private readonly float[] dashArray; + + private readonly float dashPhase; + + /// Construct PDF dash parameters. + /// + /// Numbers that specify the lengths of alternating dashes and gaps; + /// the numbers shall be nonnegative and not all zero. + /// + /// A number that specifies the distance into the dash pattern at which to start the dash. + /// + public PdfLineDashParameters(float[] dashArray, float dashPhase) { + this.dashArray = dashArray; + this.dashPhase = dashPhase; + } + + /// Return dash array. + /// dash array. + public virtual float[] GetDashArray() { + return dashArray; + } + + /// Return dash phase. + /// dash phase. + public virtual float GetDashPhase() { + return dashPhase; + } + + /// Check if some object is equal to the given object. + public override bool Equals(Object o) { + if (this == o) { + return true; + } + if (o == null || GetType() != o.GetType()) { + return false; + } + SvgStrokeParameterConverter.PdfLineDashParameters that = (SvgStrokeParameterConverter.PdfLineDashParameters + )o; + if (JavaUtil.FloatCompare(that.dashPhase, dashPhase) != 0) { + return false; + } + return JavaUtil.ArraysEquals(dashArray, that.dashArray); + } + + /// Generate a hash code for this object. + /// hash code. + public override int GetHashCode() { + int result = JavaUtil.ArraysHashCode(dashArray); + result = 31 * result + JavaUtil.FloatToIntBits(dashPhase); + return result; + } + } + } +} diff --git a/itext/itext.svg/itext/svg/logs/SvgLogMessageConstant.cs b/itext/itext.svg/itext/svg/logs/SvgLogMessageConstant.cs index 9685a6db55..036cb6b27b 100644 --- a/itext/itext.svg/itext/svg/logs/SvgLogMessageConstant.cs +++ b/itext/itext.svg/itext/svg/logs/SvgLogMessageConstant.cs @@ -49,6 +49,9 @@ public sealed class SvgLogMessageConstant { public const String PATTERN_WIDTH_OR_HEIGHT_IS_NEGATIVE = "Pattern width or height is negative value. This pattern will not be rendered."; + public const String PERCENTAGE_VALUES_IN_STROKE_DASHARRAY_AND_STROKE_DASHOFFSET_ARE_NOT_SUPPORTED = "Percentage values in 'stroke-dasharray' and 'stroke-dashoffset' attributes are not supported. " + + "Attribute will be ignored completely."; + public const String MISSING_WIDTH = "Top Svg tag has no defined width attribute and viewbox width is not present, so browser default of 300px " + "is used"; diff --git a/itext/itext.svg/itext/svg/renderers/impl/AbstractSvgNodeRenderer.cs b/itext/itext.svg/itext/svg/renderers/impl/AbstractSvgNodeRenderer.cs index 9e15757867..ee2dc36403 100644 --- a/itext/itext.svg/itext/svg/renderers/impl/AbstractSvgNodeRenderer.cs +++ b/itext/itext.svg/itext/svg/renderers/impl/AbstractSvgNodeRenderer.cs @@ -32,6 +32,7 @@ You should have received a copy of the GNU Affero General Public License using iText.StyledXmlParser.Css.Util; using iText.StyledXmlParser.Css.Validate; using iText.Svg; +using iText.Svg.Css; using iText.Svg.Css.Impl; using iText.Svg.Renderers; using iText.Svg.Utils; @@ -272,9 +273,9 @@ internal virtual void PreDraw(SvgDrawContext context) { PdfCanvas currentCanvas = context.GetCurrentCanvas(); PdfExtGState opacityGraphicsState = new PdfExtGState(); if (!partOfClipPath) { - float generalOpacity = GetOpacity(); { // fill + float generalOpacity = GetOpacity(); String fillRawValue = GetAttributeOrDefault(SvgConstants.Attributes.FILL, "black"); this.doFill = !SvgConstants.Values.NONE.EqualsIgnoreCase(fillRawValue); if (doFill && CanElementFill()) { @@ -295,36 +296,7 @@ internal virtual void PreDraw(SvgDrawContext context) { currentCanvas.SetFillColor(fillColor); } } - { - // stroke - String strokeRawValue = GetAttributeOrDefault(SvgConstants.Attributes.STROKE, SvgConstants.Values.NONE); - if (!SvgConstants.Values.NONE.EqualsIgnoreCase(strokeRawValue)) { - String strokeWidthRawValue = GetAttribute(SvgConstants.Attributes.STROKE_WIDTH); - // 1 px = 0,75 pt - float strokeWidth = 0.75f; - if (strokeWidthRawValue != null) { - strokeWidth = CssDimensionParsingUtils.ParseAbsoluteLength(strokeWidthRawValue); - } - float strokeOpacity = GetOpacityByAttributeName(SvgConstants.Attributes.STROKE_OPACITY, generalOpacity); - Color strokeColor = null; - TransparentColor transparentColor = GetColorFromAttributeValue(context, strokeRawValue, (float)((double)strokeWidth - / 2.0), strokeOpacity); - if (transparentColor != null) { - strokeColor = transparentColor.GetColor(); - strokeOpacity = transparentColor.GetOpacity(); - } - if (!CssUtils.CompareFloats(strokeOpacity, 1f)) { - opacityGraphicsState.SetStrokeOpacity(strokeOpacity); - } - // as default value for stroke is 'none' we should not set - // it in case when value obtaining fails - if (strokeColor != null) { - currentCanvas.SetStrokeColor(strokeColor); - } - currentCanvas.SetLineWidth(strokeWidth); - doStroke = true; - } - } + ApplyStrokeProperties(context, currentCanvas, opacityGraphicsState); { // opacity if (!opacityGraphicsState.GetPdfObject().IsEmpty()) { @@ -447,6 +419,45 @@ private float GetOpacity() { return result; } + private void ApplyStrokeProperties(SvgDrawContext context, PdfCanvas currentCanvas, PdfExtGState opacityGraphicsState + ) { + String strokeRawValue = GetAttributeOrDefault(SvgConstants.Attributes.STROKE, SvgConstants.Values.NONE); + if (!SvgConstants.Values.NONE.EqualsIgnoreCase(strokeRawValue)) { + String strokeWidthRawValue = GetAttribute(SvgConstants.Attributes.STROKE_WIDTH); + // 1 px = 0,75 pt + float strokeWidth = 0.75f; + if (strokeWidthRawValue != null) { + strokeWidth = CssDimensionParsingUtils.ParseAbsoluteLength(strokeWidthRawValue); + } + float generalOpacity = GetOpacity(); + float strokeOpacity = GetOpacityByAttributeName(SvgConstants.Attributes.STROKE_OPACITY, generalOpacity); + Color strokeColor = null; + TransparentColor transparentColor = GetColorFromAttributeValue(context, strokeRawValue, (float)((double)strokeWidth + / 2.0), strokeOpacity); + if (transparentColor != null) { + strokeColor = transparentColor.GetColor(); + strokeOpacity = transparentColor.GetOpacity(); + } + if (!CssUtils.CompareFloats(strokeOpacity, 1f)) { + opacityGraphicsState.SetStrokeOpacity(strokeOpacity); + } + String strokeDashArrayRawValue = GetAttribute(SvgConstants.Attributes.STROKE_DASHARRAY); + String strokeDashOffsetRawValue = GetAttribute(SvgConstants.Attributes.STROKE_DASHOFFSET); + SvgStrokeParameterConverter.PdfLineDashParameters lineDashParameters = SvgStrokeParameterConverter.ConvertStrokeDashParameters + (strokeDashArrayRawValue, strokeDashOffsetRawValue); + if (lineDashParameters != null) { + currentCanvas.SetLineDash(lineDashParameters.GetDashArray(), lineDashParameters.GetDashPhase()); + } + // as default value for stroke is 'none' we should not set + // it in case when value obtaining fails + if (strokeColor != null) { + currentCanvas.SetStrokeColor(strokeColor); + } + currentCanvas.SetLineWidth(strokeWidth); + doStroke = true; + } + } + public abstract ISvgNodeRenderer CreateDeepCopy(); public abstract Rectangle GetObjectBoundingBox(SvgDrawContext arg1); diff --git a/port-hash b/port-hash index fd0b18cd40..68fc86ab37 100644 --- a/port-hash +++ b/port-hash @@ -1 +1 @@ -9f0e82482a6a9b899cec326a9e73a6e9a0aa48d0 +4f2b6a09d50f861a4f18f665768d8c625b278c1f From 82695b70b04dc06a3f61e77dc80dc693e9588bef Mon Sep 17 00:00:00 2001 From: Guust Ysebie Date: Tue, 28 Nov 2023 14:58:36 +0100 Subject: [PATCH 2/4] Update readme --- BUILDING.md | 4 +- CODE_OF_CONDUCT.md | 8 +- CONTRIBUTING.md | 6 +- README.md | 202 ++++++++++++++++++++++++++++++------ assets/iText_Logo_Small.png | Bin 0 -> 9788 bytes 5 files changed, 178 insertions(+), 42 deletions(-) create mode 100644 assets/iText_Logo_Small.png diff --git a/BUILDING.md b/BUILDING.md index 246c5ce18b..51c3510b2c 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -14,5 +14,5 @@ environment variables. Examples of paths on Windows: If you have a new version of ImageMagick, then there is no compare.exe utility there, wrap the path to magick.exe in quotes and call compare command: ITEXT_MAGICK_COMPARE_EXEC=`"C:\Program Files\ImageMagick-7.0.9-Q16\magick.exe" compare` -[1]: http://www.ghostscript.com/ -[2]: http://www.imagemagick.org/ \ No newline at end of file +[1]: https://www.ghostscript.com/ +[2]: https://www.imagemagick.org/ \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index aabe7d251e..a41747cf5e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at http://itextpdf.com/contact. All +reported by contacting the project team at https://itextpdf.com/contact. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. @@ -68,7 +68,7 @@ members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +available at [https://contributor-covenant.org/version/1/4][version] -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +[homepage]: https://contributor-covenant.org +[version]: https://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 324d1f6db3..3491264e3c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -159,7 +159,7 @@ Please note that this project is released with a [Contributor Code of Conduct][c We use the [Stack Exchange][stackoverflow] network for free support and [GitHub][github] for code hosting. By using these services, you agree to abide by their terms: -* StackExchange: [http://stackexchange.com/legal](http://stackexchange.com/legal) +* StackExchange: [https://stackexchange.com/legal](https://stackexchange.com/legal) * Github: [https://help.github.com/articles/github-terms-of-service/](https://help.github.com/articles/github-terms-of-service/) [cla]: https://itextpdf.com/en/how-buy/legal/itext-contributor-license-agreement @@ -168,7 +168,7 @@ We use the [Stack Exchange][stackoverflow] network for free support and [GitHub] [java-style-guide]: https://www.oracle.com/technetwork/java/codeconvtoc-136057.html [javadocs]: https://itextpdf.com/api [pull]: https://github.com/itext/itext7-dotnet/pulls -[sscce]: http://sscce.org/ +[sscce]: https://sscce.org/ [stackoverflow]: https://stackoverflow.com/questions/tagged/itext [good-questions]: https://stackoverflow.com/help/how-to-ask [mcve]: https://stackoverflow.com/help/mcve @@ -180,4 +180,4 @@ We use the [Stack Exchange][stackoverflow] network for free support and [GitHub] [git-commit-end]: https://chris.beams.io/posts/git-commit/#end [git-commit-imperative]: https://chris.beams.io/posts/git-commit/#imperative [git-commit-wrap-72]: https://chris.beams.io/posts/git-commit/#wrap-72 -[git-commit-why-not-how]: https://chris.beams.io/posts/git-commit/#why-not-how \ No newline at end of file +[git-commit-why-not-how]: https://chris.beams.io/posts/git-commit/#why-not-how diff --git a/README.md b/README.md index 094d7c7115..d3cdde82a2 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,149 @@ -**[iText Community for .NET][itext]** (former iTextSharp) consists of several dlls. +

+ Logo iText +

-The **iText Core/Community** release contains: +![Nuget](https://img.shields.io/nuget/v/itext7) +[![AGPL License](https://img.shields.io/badge/license-AGPL-blue.svg)](https://github.com/itext/itext7/blob/master/LICENSE.md) +![Nuget](https://img.shields.io/nuget/dt/itext7) +![GitHub commit activity (branch)](https://img.shields.io/github/commit-activity/m/itext/itext7-dotnet) -- ```kernel.dll```: low-level functionality -- ```io.dll```: low-level functionality -- ```layout.dll```: high-level functionality. For more information see [layout overview][layoutMd]. -- ```forms.dll```: AcroForms -- ```pdfa.dll```: PDF/A-specific functionality -- ```pdftest.dll```: test helper classes -- ```barcode.dll```: use this if you want to create bar codes -- ```hyph.dll```: use this if you want text to be hyphenated -- ```font-asian.dll```: use this is you need CJK functionality (Chinese / Japanese / Korean) -- ```sign.dll```: use this if you need support for digital signatures -- ```styled-xml-parser.dll```: use this if you need support for SVG or html2pdf -- ```svg.dll```: SVG support -- ```commons.dll```: commons module -- ```bouncy-castle-connector.dll```: auxiliary internal module -- ```bouncy-castle-adapter.dll```: use this to apply BouncyCastle as low-level cryptography library -- ```bouncy-castle-fips-adapter.dll```: use this to apply BouncyCastle FIPS as low-level cryptography library +iText Core/Community is a high-performance, battle-tested library that allows you to create, adapt, +inspect and maintain PDF documents, allowing you to add PDF +functionality to your software projects with ease. It is also available for [Java](https://github.com/itext/itext7) . -The **iText Community** source code is hosted on [Github][github], where you can also [download the latest releases][latest]. +### The key features of iText Core/Community are: -*We strongly recommend that you use [NuGet][nuget] to add **iText Community** to your project:* +* Core library: + * PDF creation with the use of our layout engine + * PDF manipulation, e.g. merging multiple PDFs into one, adding new content, ... + * PDF digital signing + * PDF form creation and manipulation + * Working with PDF/A documents + * Working with PDF/UA documents + * FIPS-compliant cryptography + * Barcode generation + * SVG support +* [Addons:][all products] + * Converting XML/HTML & CSS to PDF [repo][pdfhtml], [info][pdfhtmlproduct] + * Redacting sensitive information in PDF documents [repo][pdfsweep], [info][pdfsweepproduct] + * Support for international character sets (e.g. Arabic, Chinese, Hebrew, Thai, ...) [info][calligraph] + * Optimize PDF documents for reduced file size, and increased performance [info][optimizer] + * Flattening XFA documents [info][xfa] + * PDF debugging [repo][rups], [info][rupsproduct] - Install-Package itext +Want to discover what's possible? Head over to our [Demo Lab](https://itextpdf.com/demos)! It contains a collection of +demo applications ready to use online! -You can also [build iText Community from source][building]. +### Getting started -We also provide opensource add-ons and tools to complement the core functionality: -- [pdfHTML][pdfhtml] — allows you to easily convert HTML to PDF or iText objects -- [pdfSweep][pdfsweep] — a highly efficient PDF tool to merge, split and redact data -- [RUPS][rups] — a Java tool that can help you debug PDFs +The easiest way to get started is to use NuGet, just execute the following install command in the folder of your project: -If you have an idea on how to improve **iText Community** and you want to submit code, -please read our [Contribution Guidelines][contributing]. +```shell +dotnet add package itext --version 8.0.2 +dotnet add package itext.bouncy-castle-adapter --version 8.0.2 +``` + +For more advanced use cases, please refer to the [Installation guidelines](https://kb.itextpdf.com/home/it7kb/installation-guidelines). +You can also [build iText Community from source][building]. + +### Hello PDF! + +The following example shows how easy it is to create a simple PDF document: + +```csharp +using iText.Kernel.Pdf; +using iText.Layout; +using iText.Layout.Element; + +namespace HelloPdf { + class Program { + static void Main(string[] args) { + using var document = new Document(new PdfDocument(new PdfWriter("helloworld-pdf.pdf"))); + document.Add(new Paragraph("Hello World!")); + } + } +} +``` + +### Examples + +For more advanced examples, refer to our [Knowledge Base](https://kb.itextpdf.com/home/it7kb/examples) or the main [Examples repo](https://github.com/itext/i7ns-samples). You can find C# equivalents to the Java [Signing examples](https://github.com/itext/i7js-signing-examples) [here](https://github.com/itext/i7ns-samples/tree/develop/itext/itext.publications), though the Java code is very similar since they have the same API. + + +Some of the output PDF files will be incorrectly displayed by the GitHub previewer, so be sure to download them to see +the correct +results. + +| Description | Link | +|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Basic layout** | | +| Change text properties | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/layout/ParagraphTextWithStyle.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/layout/cmp_paragraphTextWithStyle.pdf) | +| Creating a simple table | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/tables/SimpleTable9.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/tables/cmp_simple_table9.pdf) | +| Add an image to a PDF document | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/images/MultipleImages.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/images/cmp_multiple_images.pdf) | +| Create a list | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/objects/NestedLists.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/objects/cmp_nested_list.pdf) | +| Add a watermark | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/events/Watermarking.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/events/cmp_watermarkings.pdf) | +| Add links to navigate within a document | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/annotations/AddLinkAnnotation5.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/annotations/cmp_add_link_annotation5.pdf) | +| Create a popup annotation | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/annotations/MovePopup.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/annotations/cmp_move_popup.pdf) | +| Change font | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/layout/ParagraphTextWithStyle.cs) | +| Add form fields | [C#](https://kb.itextpdf.com/home/it7kb/examples/forms-in-itext-core-8-0-0) | +
| | +| **General document settings** | | +| Change page size and margin | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/layout/PageSizeAndMargins.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/layout/cmp_pageSizeAndMargins.pdf) | +| Write PDF to byte array instead of to disk | [C#](https://stackoverflow.com/a/67411657/10015628) | +| Change page rotation | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/events/PageRotation.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/events/cmp_page_rotation.pdf) | +| Add header and footer | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/events/TextFooter.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/events/cmp_text_footer.pdf) | +| Merge documents | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/merge/AddCover1.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/merge/cmp_add_cover.pdf) | +| Flatten annotations | [C#](https://kb.itextpdf.com/home/it7kb/examples/high-level-annotation-flattening) | +|
| | +| **PDF/UA, PDF/A** | | +| Create PDF/UA document | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/pdfua/PdfUA.cs), [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/pdfua/cmp_pdf_ua.pdf) | +| Create PDF/A-3 document | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/pdfa/PdfA3.cs) | +|
| | +| **FIPS** | | +| Enable FIPS | [C#](https://kb.itextpdf.com/home/it7kb/releases/release-itext-core-8-0-0/breaking-changes-for-itext-core-8-0-0/bouncy-castle-changes) | +| FIPS SHA3 example | [C#](https://kb.itextpdf.com/home/it7kb/examples/fips-sha3-examples-for-itext-core-8-0-0) | +|
| | +| **Convert HTML and CSS to PDF** | [Link to repo](https://github.com/itext/i7j-pdfhtml) | +| Convert simple HTML doc to PDF | [C#](https://kb.itextpdf.com/home/it7kb/ebooks/itext-7-converting-html-to-pdf-with-pdfhtml) | +|
| | +| **Secure redaction of content** | [Link to repo](https://github.com/itext/i7j-pdfsweep) | +| Redacting content | [C#](https://kb.itextpdf.com/home/it7kb/examples/removing-content-with-pdfsweep) | +| Redact based on regex | [C#](https://itextpdf.com/products/pdf-redaction-pdfsweep) | +|
| | +| **Support complex writing systems** | [Link to docs](https://itextpdf.com/products/pdfcalligraph) | +| Add Arabic text | [C#](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/itext/samples/sandbox/typography/arabic/ArabicWordSpacing.cs) , [PDF](https://github.com/itext/i7ns-samples/blob/master/itext/itext.samples/cmpfiles/sandbox/typography/cmp_ArabicWordSpacing.pdf) | +|
| | +| **Optimizing PDFs** | [Link to docs](https://itextpdf.com/products/compress-pdf-pdfoptimizer) | +| Reduce size of PDF | [C#](https://itextpdf.com/products/compress-pdf-pdfoptimizer) | +|
| | +| **XFA flattening** | [Link to docs](https://itextpdf.com/products/flatten-pdf-pdfxfa) | +| Flatten an XFA document | [C#](https://itextpdf.com/products/flatten-pdf-pdfxfa) | +|
| | +| **RUPS** | [Link to repo](https://github.com/itext/i7j-rups) | +| Debug a PDF | [C#](https://github.com/itext/i7j-rups/releases/latest) | + +### FAQs, tutorials, etc. ### + +Check out the [iText Knowledge Base](https://kb.itextpdf.com) for the [iText Jump-start tutorial](https://kb.itextpdf.com/home/it7kb/ebooks/itext-jump-start-tutorial-for-net) and other +tutorials, [FAQs](https://kb.itextpdf.com/home/it7kb/faq) and more. For specific information and examples relating to +digital signatures and iText, make sure to check +the [Digital Signatures Hub](https://kb.itextpdf.com/home/it7kb/digital-signatures-hub). + +Many common questions have already been answered +on [Stack Overflow](https://stackoverflow.com/questions/tagged/itext+itext7), so make sure to also check there. + +### Contributing + +Many people have contributed to **iText Core/Community** over the years. If you've found a bug, a mistake in +documentation, or have a hot new feature you want to implement, we welcome your contributions. + +Small changes or fixes can be submitted as a [Pull Request](https://github.com/itext/itext7-dotnet/pulls), while for +major changes we request you contact us at community@apryse.com so we can better coordinate our efforts and prevent +duplication of work. + +Please read our [Contribution Guidelines][contributing] for details on code submissions, coding rules, and more. + +### Licensing **iText** is dual licensed as [AGPL][agpl]/[Commercial software][sales]. @@ -53,15 +163,41 @@ These activities include: Contact [sales] for more info. [agpl]: LICENSE.md + [building]: BUILDING.md + [contributing]: CONTRIBUTING.md -[layoutMd]: itext/itext.layout/MODULE_OVERVIEW.md + +[layoutMd]: layout/README.md + [itext]: https://itextpdf.com/ -[github]: https://github.com/itext/itext7-dotnet -[latest]: https://github.com/itext/itext7-dotnet/releases/latest -[nuget]: https://www.nuget.org/packages/itext + +[github]: https://github.com/itext/itext7 + +[latest]: https://github.com/itext/itext7/releases/latest + [sales]: https://itextpdf.com/sales + [gratis]: https://en.wikipedia.org/wiki/Gratis_versus_libre + [rups]: https://github.com/itext/i7j-rups + [pdfhtml]: https://github.com/itext/i7n-pdfhtml -[pdfsweep]: https://github.com/itext/i7n-pdfsweep \ No newline at end of file + +[pdfsweep]: https://github.com/itext/i7n-pdfsweep + +[itext7net]: https://github.com/itext/itext7-dotnet + +[pdfsweepproduct]: https://itextpdf.com/products/pdf-redaction-pdfsweep + +[optimizer]: https://itextpdf.com/products/compress-pdf-pdfoptimizer + +[all products]: https://itextpdf.com/products + +[pdfhtmlproduct]: https://itextpdf.com/products/itext-pdf-html + +[xfa]: https://itextpdf.com/products/flatten-pdf-pdfxfa + +[rupsproduct]: https://itextpdf.com/products/rups + +[calligraph]: https://itextpdf.com/products/pdfcalligraph diff --git a/assets/iText_Logo_Small.png b/assets/iText_Logo_Small.png new file mode 100644 index 0000000000000000000000000000000000000000..5e6bdb9656074b130f90ad1a0a6b37d52449c759 GIT binary patch literal 9788 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 Date: Thu, 30 Nov 2023 10:59:14 +0000 Subject: [PATCH 3/4] Update readme Autoported commit. Original commit hash: [5178a6001] Manual files: BUILDING.md CODE_OF_CONDUCT.md CONTRIBUTING.md README.md assets/iText_Logo_Small.png --- port-hash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port-hash b/port-hash index 68fc86ab37..851aad46c5 100644 --- a/port-hash +++ b/port-hash @@ -1 +1 @@ -4f2b6a09d50f861a4f18f665768d8c625b278c1f +5178a60013398ed19091270bfb1972e6207c3e1f From a04b4dd4762be668eecd527062a0764c9baf1031 Mon Sep 17 00:00:00 2001 From: Eugene Bochilo Date: Wed, 29 Nov 2023 22:10:51 +0000 Subject: [PATCH 4/4] Improve default estimation size in TSAClientBouncyCastle DEVSIX-7923 Autoported commit. Original commit hash: [bf4dea17d] Manual files: bouncy-castle-adapter/src/main/java/com/itextpdf/bouncycastle/BouncyCastleFactory.java bouncy-castle-connector/src/main/java/com/itextpdf/bouncycastleconnector/BouncyCastleDefaultFactory.java bouncy-castle-fips-adapter/src/main/java/com/itextpdf/bouncycastlefips/BouncyCastleFipsFactory.java commons/src/main/java/com/itextpdf/commons/bouncycastle/IBouncyCastleFactory.java sign/src/test/java/com/itextpdf/signatures/sign/PdfPadesSignerTest.java --- .../signatures/TSAClientBouncyCastleTest.cs | 8 ++-- .../signatures/sign/PdfPadesSignerTest.cs | 37 +++++++++++++++++++ .../itext/bouncycastle/BouncyCastleFactory.cs | 5 +++ .../BouncyCastleDefaultFactory.cs | 5 +++ .../BouncyCastleFipsFactory.cs | 5 +++ .../bouncycastle/IBouncyCastleFactory.cs | 2 + itext/itext.sign/itext/signatures/PdfPKCS7.cs | 2 +- .../itext.sign/itext/signatures/PdfSigner.cs | 5 ++- .../itext/signatures/TSAClientBouncyCastle.cs | 22 ++++++----- .../SignExceptionMessageConstant.cs | 4 ++ port-hash | 2 +- 11 files changed, 81 insertions(+), 16 deletions(-) diff --git a/itext.tests/itext.sign.tests/itext/signatures/TSAClientBouncyCastleTest.cs b/itext.tests/itext.sign.tests/itext/signatures/TSAClientBouncyCastleTest.cs index ea4d869ec6..c5ec4af282 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/TSAClientBouncyCastleTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/TSAClientBouncyCastleTest.cs @@ -58,8 +58,8 @@ public virtual void TestTsaClientBouncyCastleConstructor3Args() { NUnit.Framework.Assert.AreEqual(url, tsaClientBouncyCastle.tsaURL); NUnit.Framework.Assert.AreEqual(userName, tsaClientBouncyCastle.tsaUsername); NUnit.Framework.Assert.AreEqual(password, tsaClientBouncyCastle.tsaPassword); - NUnit.Framework.Assert.AreEqual(TSAClientBouncyCastle.DEFAULTTOKENSIZE, tsaClientBouncyCastle.tokenSizeEstimate - ); + NUnit.Framework.Assert.AreEqual(TSAClientBouncyCastle.DEFAULTTOKENSIZE, tsaClientBouncyCastle.GetTokenSizeEstimate + ()); NUnit.Framework.Assert.AreEqual(TSAClientBouncyCastle.DEFAULTHASHALGORITHM, tsaClientBouncyCastle.digestAlgorithm ); } @@ -76,7 +76,9 @@ public virtual void TestTsaClientBouncyCastleConstructorAllArgs() { NUnit.Framework.Assert.AreEqual(url, tsaClientBouncyCastle.tsaURL); NUnit.Framework.Assert.AreEqual(userName, tsaClientBouncyCastle.tsaUsername); NUnit.Framework.Assert.AreEqual(password, tsaClientBouncyCastle.tsaPassword); - NUnit.Framework.Assert.AreEqual(tokenSize, tsaClientBouncyCastle.tokenSizeEstimate); + NUnit.Framework.Assert.AreEqual(TSAClientBouncyCastle.DEFAULTTOKENSIZE, tsaClientBouncyCastle.tokenSizeEstimate + ); + NUnit.Framework.Assert.AreEqual(tokenSize, tsaClientBouncyCastle.GetTokenSizeEstimate()); NUnit.Framework.Assert.AreEqual(digestAlgorithm, tsaClientBouncyCastle.digestAlgorithm); } diff --git a/itext.tests/itext.sign.tests/itext/signatures/sign/PdfPadesSignerTest.cs b/itext.tests/itext.sign.tests/itext/signatures/sign/PdfPadesSignerTest.cs index d0150a2470..a1ed4de0ff 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/sign/PdfPadesSignerTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/sign/PdfPadesSignerTest.cs @@ -21,6 +21,8 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ using System; +using System.Collections.Generic; +using System.IO; using iText.Bouncycastleconnector; using iText.Commons.Bouncycastle; using iText.Commons.Bouncycastle.Cert; @@ -156,6 +158,31 @@ public virtual void DefaultSignerPropertiesTest() { TestSignUtils.BasicCheckSignedDoc(outFileName, "Signature1"); NUnit.Framework.Assert.IsNull(SignaturesCompareTool.CompareSignatures(outFileName, cmpFileName)); } + + [NUnit.Framework.Test] + public virtual void SmallTokenSizeEstimationTest() { + String fileName = "smallTokenSizeEstimationTest.pdf"; + String outFileName = destinationFolder + fileName; + String srcFileName = sourceFolder + "helloWorldDoc.pdf"; + String signCertFileName = certsSrc + "signCertRsa01.pem"; + String tsaCertFileName = certsSrc + "tsCertRsa.pem"; + String caCertFileName = certsSrc + "rootRsa.pem"; + IX509Certificate[] signRsaChain = PemFileHelper.ReadFirstChain(signCertFileName); + IPrivateKey signRsaPrivateKey = PemFileHelper.ReadFirstKey(signCertFileName, password); + IExternalSignature pks = new PrivateKeySignature(signRsaPrivateKey, DigestAlgorithms.SHA256); + IX509Certificate[] tsaChain = PemFileHelper.ReadFirstChain(tsaCertFileName); + IPrivateKey tsaPrivateKey = PemFileHelper.ReadFirstKey(tsaCertFileName, password); + IX509Certificate caCert = (IX509Certificate)PemFileHelper.ReadFirstChain(caCertFileName)[0]; + IPrivateKey caPrivateKey = PemFileHelper.ReadFirstKey(caCertFileName, password); + SignerProperties signerProperties = new SignerProperties(); + PdfPadesSigner padesSigner = CreatePdfPadesSigner(srcFileName, outFileName); + TestTsaClient testTsa = new TestTsaClientWithCustomSizeEstimation(JavaUtil.ArraysAsList(tsaChain), tsaPrivateKey); + ICrlClient crlClient = new TestCrlClient().AddBuilderForCertIssuer(caCert, caPrivateKey); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, caPrivateKey); + padesSigner.SetOcspClient(ocspClient).SetCrlClient(crlClient); + Exception exception = NUnit.Framework.Assert.Catch(typeof(IOException), () => padesSigner.SignWithBaselineLTAProfile + (signerProperties, signRsaChain, pks, testTsa)); + } private SignerProperties CreateSignerProperties() { SignerProperties signerProperties = new SignerProperties(); @@ -170,5 +197,15 @@ private PdfPadesSigner CreatePdfPadesSigner(String srcFileName, String outFileNa return new PdfPadesSigner(new PdfReader(FileUtil.GetInputStreamForFile(srcFileName)), FileUtil.GetFileOutputStream (outFileName)); } + + private sealed class TestTsaClientWithCustomSizeEstimation : TestTsaClient { + public TestTsaClientWithCustomSizeEstimation(IList tsaCertificateChain, + IPrivateKey tsaPrivateKey) : base(tsaCertificateChain, tsaPrivateKey) { + } + + public override int GetTokenSizeEstimate() { + return 1024; + } + } } } diff --git a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/BouncyCastleFactory.cs b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/BouncyCastleFactory.cs index 27926e650c..5d6bdc5cf0 100644 --- a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/BouncyCastleFactory.cs +++ b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/BouncyCastleFactory.cs @@ -1036,6 +1036,11 @@ public ISubjectKeyIdentifier CreateSubjectKeyIdentifier(ISubjectPublicKeyInfo su public bool IsNullExtension(IX509Extension ext) { return ((X509ExtensionBC)ext).GetX509Extension() == null; } + + /// + public bool IsNull(IAsn1Encodable encodable) { + return ((Asn1EncodableBC)encodable).GetEncodable() == null; + } /// public IX509Extension CreateExtension(bool b, IDerOctetString octetString) { diff --git a/itext/itext.bouncy-castle-connector/itext/bouncycastleconnector/BouncyCastleDefaultFactory.cs b/itext/itext.bouncy-castle-connector/itext/bouncycastleconnector/BouncyCastleDefaultFactory.cs index 894bbca99a..14d4ee62dc 100644 --- a/itext/itext.bouncy-castle-connector/itext/bouncycastleconnector/BouncyCastleDefaultFactory.cs +++ b/itext/itext.bouncy-castle-connector/itext/bouncycastleconnector/BouncyCastleDefaultFactory.cs @@ -653,6 +653,11 @@ public IX509Extension CreateExtension(bool b, IDerOctetString octetString) { public bool IsNullExtension(IX509Extension extNonce) { throw new NotSupportedException(BouncyCastleLogMessageConstant.BOUNCY_CASTLE_DEPENDENCY_MUST_PRESENT); } + + + public bool IsNull(IAsn1Encodable encodable) { + throw new NotSupportedException(BouncyCastleLogMessageConstant.BOUNCY_CASTLE_DEPENDENCY_MUST_PRESENT); + } public byte[] CreateCipherBytes(IX509Certificate x509Certificate, byte[] abyte0, IAlgorithmIdentifier algorithmidentifier) { throw new NotSupportedException(BouncyCastleLogMessageConstant.BOUNCY_CASTLE_DEPENDENCY_MUST_PRESENT); diff --git a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/BouncyCastleFipsFactory.cs b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/BouncyCastleFipsFactory.cs index 6e17e0fcc9..7f2e634f59 100644 --- a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/BouncyCastleFipsFactory.cs +++ b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/BouncyCastleFipsFactory.cs @@ -1100,6 +1100,11 @@ public ISubjectKeyIdentifier CreateSubjectKeyIdentifier(ISubjectPublicKeyInfo su public bool IsNullExtension(IX509Extension ext) { return ((X509ExtensionBCFips)ext).GetX509Extension() == null; } + + /// + public bool IsNull(IAsn1Encodable encodable) { + return ((Asn1EncodableBCFips)encodable).GetEncodable() == null; + } /// public IX509Extension CreateExtension(bool b, IDerOctetString octetString) { diff --git a/itext/itext.commons/itext/commons/bouncycastle/IBouncyCastleFactory.cs b/itext/itext.commons/itext/commons/bouncycastle/IBouncyCastleFactory.cs index ea6680cd23..edee28efb7 100644 --- a/itext/itext.commons/itext/commons/bouncycastle/IBouncyCastleFactory.cs +++ b/itext/itext.commons/itext/commons/bouncycastle/IBouncyCastleFactory.cs @@ -1470,6 +1470,8 @@ IX509V3CertificateGenerator CreateJcaX509v3CertificateBuilder(IX509Certificate s /// otherwise /// bool IsNullExtension(IX509Extension extNonce); + + bool IsNull(IAsn1Encodable encodable); /// /// Create diff --git a/itext/itext.sign/itext/signatures/PdfPKCS7.cs b/itext/itext.sign/itext/signatures/PdfPKCS7.cs index a8c5f97d09..6b9ba4d645 100644 --- a/itext/itext.sign/itext/signatures/PdfPKCS7.cs +++ b/itext/itext.sign/itext/signatures/PdfPKCS7.cs @@ -328,7 +328,7 @@ public PdfPKCS7(byte[] contentsKey, PdfName filterSubtype) { IAttributeTable attble = BOUNCY_CASTLE_FACTORY.CreateAttributeTable(unat); IPkcsObjectIdentifiers ipkcsObjectIdentifiers = BOUNCY_CASTLE_FACTORY.CreatePKCSObjectIdentifiers(); IAttribute ts = attble.Get(ipkcsObjectIdentifiers.GetIdAaSignatureTimeStampToken()); - if (ts != null && ts.GetAttrValues().Size() > 0) { + if (!BOUNCY_CASTLE_FACTORY.IsNull(ts) && ts.GetAttrValues().Size() > 0) { IAsn1Set attributeValues = ts.GetAttrValues(); IAsn1Sequence tokenSequence = BOUNCY_CASTLE_FACTORY.CreateASN1SequenceInstance(attributeValues.GetObjectAt (0)); diff --git a/itext/itext.sign/itext/signatures/PdfSigner.cs b/itext/itext.sign/itext/signatures/PdfSigner.cs index 634e8567aa..1889c3ba85 100644 --- a/itext/itext.sign/itext/signatures/PdfSigner.cs +++ b/itext/itext.sign/itext/signatures/PdfSigner.cs @@ -591,7 +591,7 @@ public virtual void SignDetached(IExternalSignature externalSignature, IX509Cert estimatedSize += 4192; } if (tsaClient != null) { - estimatedSize += 4192; + estimatedSize += tsaClient.GetTokenSizeEstimate() + 96; } } appearance.SetCertificate(chain[0]); @@ -732,7 +732,8 @@ public virtual void Timestamp(ITSAClient tsa, String signatureName) { .Message, e); } if (contentEstimated + 2 < tsToken.Length) { - throw new System.IO.IOException("Not enough space"); + throw new System.IO.IOException(MessageFormatUtil.Format(SignExceptionMessageConstant.TOKEN_ESTIMATION_SIZE_IS_NOT_LARGE_ENOUGH + , contentEstimated, tsToken.Length)); } byte[] paddedSig = new byte[contentEstimated]; Array.Copy(tsToken, 0, paddedSig, 0, tsToken.Length); diff --git a/itext/itext.sign/itext/signatures/TSAClientBouncyCastle.cs b/itext/itext.sign/itext/signatures/TSAClientBouncyCastle.cs index 039fc851d9..512d0c608e 100644 --- a/itext/itext.sign/itext/signatures/TSAClientBouncyCastle.cs +++ b/itext/itext.sign/itext/signatures/TSAClientBouncyCastle.cs @@ -53,8 +53,8 @@ public class TSAClientBouncyCastle : ITSAClient { /// The default value for the hash algorithm public const String DEFAULTHASHALGORITHM = "SHA-256"; - /// The default value for the hash algorithm - public const int DEFAULTTOKENSIZE = 4096; + /// The default value for token size estimation. + public const int DEFAULTTOKENSIZE = 10240; /// The Logger instance. private static readonly ILogger LOGGER = ITextLogManager.GetLogger(typeof(iText.Signatures.TSAClientBouncyCastle @@ -73,26 +73,30 @@ public class TSAClientBouncyCastle : ITSAClient { protected internal ITSAInfoBouncyCastle tsaInfo; /// Estimate of the received time stamp token - protected internal int tokenSizeEstimate; + protected internal int tokenSizeEstimate = DEFAULTTOKENSIZE; /// Hash algorithm - protected internal String digestAlgorithm; + protected internal String digestAlgorithm = DEFAULTHASHALGORITHM; /// TSA request policy private String tsaReqPolicy; + private int customTokenSizeEstimate = -1; + /// Creates an instance of a TSAClient that will use BouncyCastle. /// String - Time Stamp Authority URL (i.e. "http://tsatest1.digistamp.com/TSA") public TSAClientBouncyCastle(String url) - : this(url, null, null, DEFAULTTOKENSIZE, DEFAULTHASHALGORITHM) { + : this(url, null, null) { } /// Creates an instance of a TSAClient that will use BouncyCastle. /// String - Time Stamp Authority URL (i.e. "http://tsatest1.digistamp.com/TSA") /// String - user(account) name /// String - password - public TSAClientBouncyCastle(String url, String username, String password) - : this(url, username, password, 4096, DEFAULTHASHALGORITHM) { + public TSAClientBouncyCastle(String url, String username, String password) { + this.tsaURL = url; + this.tsaUsername = username; + this.tsaPassword = password; } /// Constructor. @@ -115,7 +119,7 @@ public TSAClientBouncyCastle(String url, String username, String password, int t this.tsaURL = url; this.tsaUsername = username; this.tsaPassword = password; - this.tokenSizeEstimate = tokSzEstimate; + this.customTokenSizeEstimate = tokSzEstimate; this.digestAlgorithm = digestAlgorithm; } @@ -131,7 +135,7 @@ public virtual void SetTSAInfo(ITSAInfoBouncyCastle tsaInfo) { /// /// an estimate of the token size public virtual int GetTokenSizeEstimate() { - return tokenSizeEstimate; + return customTokenSizeEstimate == -1 ? tokenSizeEstimate : customTokenSizeEstimate; } /// Gets the TSA request policy that will be used when retrieving timestamp token. diff --git a/itext/itext.sign/itext/signatures/exceptions/SignExceptionMessageConstant.cs b/itext/itext.sign/itext/signatures/exceptions/SignExceptionMessageConstant.cs index b090c15eb5..06375f14a9 100644 --- a/itext/itext.sign/itext/signatures/exceptions/SignExceptionMessageConstant.cs +++ b/itext/itext.sign/itext/signatures/exceptions/SignExceptionMessageConstant.cs @@ -32,6 +32,10 @@ public sealed class SignExceptionMessageConstant { public const String AVAILABLE_SPACE_IS_NOT_ENOUGH_FOR_SIGNATURE = "Available space is not enough for " + "signature."; + public const String TOKEN_ESTIMATION_SIZE_IS_NOT_LARGE_ENOUGH = "Timestamp token estimation size is not " + + "large enough to accommodate the entire timestamp token. Timestamp token estimation size is: {0} bytes, " + + "however real timestamp token size is: {1} bytes."; + public const String CANNOT_DECODE_PKCS7_SIGNED_DATA_OBJECT = "Cannot decode PKCS#7 SignedData object."; public const String CANNOT_FIND_SIGNING_CERTIFICATE_WITH_THIS_SERIAL = "Cannot find signing certificate " diff --git a/port-hash b/port-hash index 851aad46c5..e527693454 100644 --- a/port-hash +++ b/port-hash @@ -1 +1 @@ -5178a60013398ed19091270bfb1972e6207c3e1f +595a3a23a5c6446b66606a6cfa0ccddccdb43400 \ No newline at end of file