From 9f56f08608885303adac16a0fbe31ce400915346 Mon Sep 17 00:00:00 2001 From: smilerightnow Date: Wed, 4 Sep 2024 14:35:08 +0100 Subject: [PATCH] Fixing issue #1236 --- docs/TextStyling.md | 2 +- fpdf/fpdf.py | 40 ++++----- .../cell_markdown_bold_italic_escaped.pdf | Bin 1128 -> 1130 bytes test/text/cell_markdown_escaped.pdf | Bin 1161 -> 1163 bytes .../cell_markdown_with_ttf_fonts_escaped.pdf | Bin 17055 -> 17096 bytes test/text/multi_cell_markdown_escaped.pdf | Bin 1311 -> 1178 bytes ...i_cell_markdown_with_ttf_fonts_escaped.pdf | Bin 19112 -> 17684 bytes test/text/test_cell.py | 2 + test/text/test_markdown_parse.py | 82 +++++++++++++----- 9 files changed, 79 insertions(+), 47 deletions(-) diff --git a/docs/TextStyling.md b/docs/TextStyling.md index a53db1635..4990e4fac 100644 --- a/docs/TextStyling.md +++ b/docs/TextStyling.md @@ -194,7 +194,7 @@ pdf = FPDF() pdf.add_page() pdf.set_font("Times", size=50) pdf.cell(text="**Lorem** __Ipsum__ --dolor--", markdown=True, new_x='LEFT', new_y='NEXT') -pdf.cell(text="\\**Lorem\\** \\\\__Ipsum\\\\__ --dolor--", markdown=True) +pdf.cell(text="\\**Lorem\\** __\\Ipsum\\ __ --dolor--", markdown=True) pdf.output("markdown-styled.pdf") ``` diff --git a/fpdf/fpdf.py b/fpdf/fpdf.py index 25a048bdd..e2033311b 100644 --- a/fpdf/fpdf.py +++ b/fpdf/fpdf.py @@ -3456,9 +3456,16 @@ def frag(): font_glyphs = self.current_font.cmap else: font_glyphs = [] - num_escape_chars = 0 while text: + tlt = text[:3] ## get triples to check for escape character + if self.MARKDOWN_ESCAPE_CHARACTER == tlt[0] and tlt[1:] in ["**", "__", "--"]: + text = text[1:] ## remove the escape character + for i in range(2): + txt_frag.append(text[0]) + text = text[1:] + yield frag() + continue is_marker = text[:2] in ( self.MARKDOWN_BOLD_MARKER, self.MARKDOWN_ITALICS_MARKER, @@ -3482,27 +3489,16 @@ def frag(): and (not txt_frag or txt_frag[-1] != half_marker) and (len(text) < 3 or text[2] != half_marker) ): - txt_frag = ( - txt_frag[: -((num_escape_chars + 1) // 2)] - if num_escape_chars > 0 - else txt_frag - ) - if num_escape_chars % 2 == 0: - if txt_frag: - yield frag() - if text[:2] == self.MARKDOWN_BOLD_MARKER: - in_bold = not in_bold - if text[:2] == self.MARKDOWN_ITALICS_MARKER: - in_italics = not in_italics - if text[:2] == self.MARKDOWN_UNDERLINE_MARKER: - in_underline = not in_underline - text = text[2:] - continue - num_escape_chars = ( - num_escape_chars + 1 - if text[0] == self.MARKDOWN_ESCAPE_CHARACTER - else 0 - ) + if txt_frag: + yield frag() + if text[:2] == self.MARKDOWN_BOLD_MARKER: + in_bold = not in_bold + if text[:2] == self.MARKDOWN_ITALICS_MARKER: + in_italics = not in_italics + if text[:2] == self.MARKDOWN_UNDERLINE_MARKER: + in_underline = not in_underline + text = text[2:] + continue is_link = self.MARKDOWN_LINK_REGEX.match(text) if is_link: link_text, link_dest, text = is_link.groups() diff --git a/test/text/cell_markdown_bold_italic_escaped.pdf b/test/text/cell_markdown_bold_italic_escaped.pdf index 547ef8245f7bcabab0867bc2ba48ae50388ceaf5..2b49a4eb0f2c7cc9f44c1c7ac38a2df510cd5676 100644 GIT binary patch delta 203 zcmaFC@rq-E10$p5WJgAC$tJcV2GKS7-5K+8(U7k$ZQ>BVd?5-Y3k_c>S|(YXzA>1VeVpNYUyU_ pW@KdKYGLYZXG2g$ESH@fS8+*VQAtHnY8sb?fdQAQs;j>n7XYnlGn)Va delta 201 zcmaFG@q%N610$pPWJgAC@mE!1BGaF}dX=TslEPT3#Ztt`P_#f~>E@-39~o`U6%0T? zAy0t|%rG!8H8#T#Gcz&35HmM6pM06wI>y+=#l*na)X~t%$l2V|+|=CB&C%@me>W}wg3&Vu diff --git a/test/text/cell_markdown_escaped.pdf b/test/text/cell_markdown_escaped.pdf index ee561c2414168019e797a5d123ea0f07d711c77d..c54304866b37d421ed30e4813fb8b85dd1c67f0a 100644 GIT binary patch delta 312 zcmeC=?B?9y$XIXAWoO4#TvC*pn9Ef$$2jQ2Bclt(rr{dTi?{+WlpBW_7+x?o2;lGx zF5qS4l$3rY8Kp5JTH@8rSsE=%7^X@nPRaQ3kNrl4sNfdPo6&!U&y)0;tlW^RfhW^QgY`4h8sjGL>4vw^XJv!RirtD&W_ vtCNALfuX61xv8_0lZm5=tDOx&6|sJcjE#87u8_T delta 310 zcmeC??Bv|w$XIX6WoO4#TvC*pn9Ef$C$z_q`;dW1%lAKB(vKe)cY4O};t~}(ZqBx8 zi|i4h&W`%~ZY+g+cE7i7-)tlzp`6T0@UvH{(wg>G`jF`XMV=ue$_Sbd+FE*5Non0@S_LXh7k4n$x zGmNhpZOs%6KtLf+feXwqFfcVY#Sk+yH^vY%H#eC4h1ojB+||*^z|qOb+|bp<)Wpfc u)ZEp@(9qDr!qvph&B@W!&W50hSV*8G7L`;KrKWLN7#nh_s=E5SaRC5^e`n4B diff --git a/test/text/cell_markdown_with_ttf_fonts_escaped.pdf b/test/text/cell_markdown_with_ttf_fonts_escaped.pdf index 86e6f0c54362815c85e9498489eca04d693463aa..ec63cdfdc81a3257c4c5c745518f80dad176b903 100644 GIT binary patch delta 4680 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 EKW!97(*OVf delta 4620 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 eH^cJZUkF!2be)N+|sR diff --git a/test/text/multi_cell_markdown_escaped.pdf b/test/text/multi_cell_markdown_escaped.pdf index d862add764fd0dfff8064ba62456090c85772790..feab8db4c265bc223e40c7abc92b6c0b310f8a20 100644 GIT binary patch delta 523 zcmbQwHH&k?Z$`6;e_ZPgxa{n>ic5-86LYyL=FGit*004tr0tWgA4mMIgC-mPT9|5hzgYKO{<5UC>%_OHTc(rQ&O|)Q^>} zDUuUc+BXTED{$}ZczFH0^z5~w>wC*(3@i4v{Vra2c2h)ftayg9%Kx*6Z|YatF_f2V zd^0(YsgcJmKd(f=#zx=GP{DL^B(puE`Q(|*&Wsk5?=t7r8!8xpfI^-E7nosSU~Xi9 zA!cDq& delta 561 zcmbQmIiG97Z$|Tpe_ZR0x$Nw?ic5-86LYyL=4_p?H?P@1!u7k&QCZC_+1r2CPo1^! zRZ!=|qeuOoa4<7QSN@F>VVWAHDZ4A``t!Q)1$*9UcpAl&pI80H-qs=P6)5*H=$+n& zenDj&_i5=%Is&}{fPl(r*=JLe0uZo zhkv{?RE#GqSLwRxA`Pst@s5@tx&+_}%^XOvFslT};N}*Rp?#lnz zrLp1EmwzsIuSI3OjH>p|En_bb>;3WJ@q#zkwYGk{7j?Pm?Ak`bVf%LY@K_m|*(c2S77F?KOGa5A(AH8wLhGca&*b~11>GBGzYG_W)=c64@g gb+v;j!L1?|k`@w+N-By{)3_`x47pTQUH#p-0Kh%dFaQ7m diff --git a/test/text/multi_cell_markdown_with_ttf_fonts_escaped.pdf b/test/text/multi_cell_markdown_with_ttf_fonts_escaped.pdf index 224a0b6bafab1be352df94ab597cab643298365c..ed1b45e91420c680cb3875b9a9ee2b39ca2e4aa6 100644 GIT binary patch delta 9078 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

XXN}Xh=seW34?ySa%J9;d2nUEAaIlF* z;ie<44OCFIx*gH!tmPGV1$>fE20p}}#%jX`CE}I$A4Q4OB@;>~3gJZ1anf*#C;s{r zQPu0wa$jA?MK*ai+SSLp#`jUV)uzI$dNwpDfw}xfI^?w{b^cLXdoSiLs@1<0tZUT% z%;tBDd8k|)P^Jx#`4%XvISVPb?k{YYGU+!TNNe|}$w!jx#At->gI`y_uMUsvfH$+J zXzi{Lp{IaXZ({mdZ;0W!Jpk|O36OTIaFM@ku4qQLPy4*lu1xABm386f0d@=}y5+nR z=pi7;C7#Msav>F*w!J(EpdF!IoXA?(9Pv*`H$eM%^+_f^IF6;)pA!bYEA>R*mbY!X=^5o`e6sEK zp|lQHVx7YM)5EpyT5Lj2Hy1KYF+NT7aF8=soGy3FIfprG7wS7ufBpx(4w>ERoA|@+l|AU$dmh}O-{6c0#V;C94d|OOgBj58Ee`P=+1`s6&X(K z6-$y|se{@2Xt`TNC&M;$@T?X6^|<3BnwX5*Nsnu!YVK=ZR$4xIrVnQqZjGFfXc?r0 zmfLA!ZglltH!q0}G`OiY_GjR=bugVRwQ4Lg|Q(c1ahKLGE&mvSgc zqN^ybJyT3U)&<}`LV({fLa zE0LRUs(sUaEY8d%O#C{OxG=*prcxr! zf5k1bEaMy|cexNRl_bXy>y`!iTF{g{L@WbgL!L@V*#K<6g!ku}cuNul?s0R!Z&k?^}Phn#+y z9U0Z(EUgK0BG?(-+*zR05&eGXA)+KlI{}qGkQmD~k0erm;lQ=3-`o*J9 z(@P@O6IwZsq{y{bV0Emw-awQ!p7)$k8Gf^LottCwT;B92Fiq2w)M!p4uBF7$!*q)M zPyT(g)Ipad9INr|gvVH{Z#nWxS8&B#U?F~T)(}33qf>`rO=vCL29tbyTidp=ey-O; zKz&;%+FGSWmq$8w1RX}YYB@2n_4h%E?)aj2mm}xlQ-@^O8&@&}$7Xj-V7{Jdp0jkD zNPU5pXOFVIym(Vh!l&oD7DwI>*`bag-R z^9=2d8n`Xci!bu$I(5ASmfV}{D4(BlNlHt(IfmiN z0bt1vR@?z{dgadr*f>}?^C^U#(`h(mu6*TiYfmG1$NGArVu9WU&Ksl|77gE38Uv&t ziBBORueK}aom11(Ppx0ObqPv*T;{2uWkWtaw7|MzqOE8)*2ng=m8@Fl+HRp>yJr0E zS<&DxQ!)eP1c+3# zO)Eaj5Ab4BmZ_(Wm1nV+&Ck3_-yiQMKYaFkIPE7VZn9jBz>MGa1>16(N12_U{i+M9 zbWqstwOtm?^}XN4mzZCHNc?J4_pnXG<&eyB{RbfJ2k)ZfI~bwB+?M z$bA*J*5bxRe&>N;bhaFgTEar-lX=7V&h?(t0?!Z&DH*r(0k%Zz&bN42RWEkPi^o4t{XC z8U3Jn<2`bG(hm8MvuyAMlA?%I$&634a2WEX9SYq9)*6QLCy+60m&GgNnl#LNX-4~a z`a80?-!*E4I33Pvi0w+8LC%!I`qTRL%}oA6ZrbP8y3@Qapx-~a-?}i4HbIBtlU43> zS9N)k#P9P*`PjUDZFq-utHL00g+VB$Z|ANURA-nn4%yomjp?w1i$w@sx65q^4wi(^ z@RJTgzv(%|sql{sWl7#=A|oX55S2}F=C0Ur}d-^Dg@GAEU^P`e&SzGIBl!zAOsLj*6oscG^ z0?s@fZ-9NhL2X8lpiDzivxo{^z93&5XSj|6RO0P!Ew&95^ktSdmL19$P$e(KMT_}X zn^sqeY^J(bm(2f)9%=jNdgN!ju)N5{*G>-p=~s0cw;`)T*y@9Ke`ly>IkA-Lm!K5X z1p|WGs-=8%d7Q73)pLYA)?c$kc7Mn%3p7QQ2O~qAkd+8-W%Mf#E-?-p;rG#Pt{Tlx zs%3TAy$73a!R>H88d*gq?xWpz$J%%JVJii%P2p8}>Ggr*7xC+|>x*M+T)hM8 zT=1(6{q!7N3tQX}{7uDrT|&GceY}n01i`SiTGkY&lxaJ*B;1gP^{ho3oh8gAf)i(q zeBGO7hbJlxO`T2CLpEVo*F8O$Au ze#8zHU+n6RySzhnOiZ&ar*6tbs1}+v?o5YWjo@vK#j*N2Z9~d!{p!=5L$|4bh9&W< z-QhTmoB~K?>x~ClM84^gU~#~1tM{N`~4>$0})OZDOmJXYdZ8TZC|BWh zBl~`AS3*HF*Kd?a^1Z0gFFtnV`=Vi6v*yv!wKubUMIH>aA6>2;G$DYW-W?bxL*_oL z6YMj(+KIM;|M6tGN<96&+3{Qvk+3drFjlzp$}^!JPlCu>;uI{#*Ma0sg6h z*#Dsc!N7lLTwsoWX^{Ur7z70VW5^$W|1bjr{fX%Q?e%|6gMq+*^np3p{;6>R{_lnV z%>T2%U~ce#9mvkk&GFx^*#T_a|80;R00jSIFOdCDcKDwL+5g0f|I|1HIJo}N2m=21 z?byKp;6M8Qb>jq2F@6IHE=~X^h*Ox2i;Z2Fn}eNOf&<6~VCNJDfQ11-0G9yY|F=W` bZxm?k{Mp0V+yaFi4CDr((9no0N}&8do}Rtu diff --git a/test/text/test_cell.py b/test/text/test_cell.py index eda447f67..950e0d855 100644 --- a/test/text/test_cell.py +++ b/test/text/test_cell.py @@ -178,6 +178,8 @@ def test_cell_markdown_escaped(tmp_path): pdf.add_page() pdf.set_font("Times", size=40) pdf.cell(text="**Lo\\rem** \\__Ipsum\\__ \\\\--dolor\\\\--", markdown=True) + pdf.write(text="\n") + pdf.cell(text="\\****BOLD**\\**", markdown=True) assert_pdf_equal(pdf, HERE / "cell_markdown_escaped.pdf", tmp_path) diff --git a/test/text/test_markdown_parse.py b/test/text/test_markdown_parse.py index cfda8328c..145830272 100644 --- a/test/text/test_markdown_parse.py +++ b/test/text/test_markdown_parse.py @@ -13,6 +13,40 @@ GSTATE_BI = GSTATE.copy() GSTATE_BI["font_style"] = "BI" +def merge_fragments(fragments): + """ + Helper function for testing the escaping chracters + + Will merge fragments that have different characters but same fragment.graphics_state + and same fragment.k and same fragment.link. + + Example Input: + + ( + Fragment(characters=['a'], graphics_state={000}, k=1, link=None), + Fragment(characters=['b'], graphics_state={000}, k=1, link=None) + ) + + Example Output: + (Fragment(characters=['a', 'b'], graphics_state={000}, k=1, link=None)) + + """ + if not fragments: + return [] + + merged_fragments = [] + current_fragment = fragments[0] + + for fragment in fragments[1:]: + if fragment.graphics_state == current_fragment.graphics_state and fragment.k == current_fragment.k and fragment.link == current_fragment.link: + current_fragment.characters.extend(fragment.characters) + else: + merged_fragments.append(current_fragment) + current_fragment = fragment + + merged_fragments.append(current_fragment) + + return tuple(merged_fragments) def test_markdown_parse_simple_ok(): frags = tuple(FPDF()._parse_chars("**bold**, __italics__ and --underlined--", True)) @@ -27,30 +61,30 @@ def test_markdown_parse_simple_ok(): def test_markdown_parse_simple_ok_escaped(): - frags = tuple( + frags = merge_fragments(tuple( FPDF()._parse_chars( "\\**bold\\**, \\__italics\\__ and \\--underlined\\-- escaped", True ) - ) + )) expected = ( Fragment("**bold**, __italics__ and --underlined-- escaped", GSTATE, k=PDF.k), ) assert frags == expected - frags = tuple( + frags = merge_fragments(tuple( FPDF()._parse_chars( r"raw \**bold\**, \__italics\__ and \--underlined\-- escaped", True ) - ) + )) expected = ( Fragment( "raw **bold**, __italics__ and --underlined-- escaped", GSTATE, k=PDF.k ), ) assert frags == expected - frags = tuple(FPDF()._parse_chars("escape *\\*between marker*\\*", True)) + frags = merge_fragments(tuple(FPDF()._parse_chars("escape *\\*between marker*\\*", True))) expected = (Fragment("escape *\\*between marker*\\*", GSTATE, k=PDF.k),) assert frags == expected - frags = tuple(FPDF()._parse_chars("escape **\\after marker**\\", True)) + frags = merge_fragments(tuple(FPDF()._parse_chars("escape **\\after marker**\\", True))) expected = ( Fragment("escape ", GSTATE, k=PDF.k), Fragment("\\after marker", GSTATE_B, k=PDF.k), @@ -59,26 +93,22 @@ def test_markdown_parse_simple_ok_escaped(): def test_markdown_unrelated_escape(): - frags = tuple(FPDF()._parse_chars("unrelated \\ escape \\**bold\\**", True)) + frags = merge_fragments(tuple(FPDF()._parse_chars("unrelated \\ escape \\**bold\\**", True))) expected = (Fragment("unrelated \\ escape **bold**", GSTATE, k=PDF.k),) assert frags == expected - frags = tuple( + frags = merge_fragments(tuple( FPDF()._parse_chars("unrelated \\\\ double escape \\**bold\\**", True) - ) + )) expected = (Fragment("unrelated \\\\ double escape **bold**", GSTATE, k=PDF.k),) assert frags == expected def test_markdown_parse_multiple_escape(): - frags = tuple(FPDF()._parse_chars("\\\\**bold\\\\** double escaped", True)) - expected = ( - Fragment("\\", GSTATE, k=PDF.k), - Fragment("bold\\", GSTATE_B, k=PDF.k), - Fragment(" double escaped", GSTATE, k=PDF.k), - ) + frags = merge_fragments(tuple(FPDF()._parse_chars("\\\\**bold\\\\** double escaped", True))) + expected = (Fragment("\\**bold\\** double escaped", GSTATE, k=PDF.k),) assert frags == expected - frags = tuple(FPDF()._parse_chars("\\\\\\**triple bold\\\\\\** escaped", True)) - expected = (Fragment("\\**triple bold\\** escaped", GSTATE, k=PDF.k),) + frags = merge_fragments(tuple(FPDF()._parse_chars("\\\\\\**triple bold\\\\\\** escaped", True))) + expected = (Fragment("\\\\**triple bold\\\\** escaped", GSTATE, k=PDF.k),) assert frags == expected @@ -92,7 +122,7 @@ def test_markdown_parse_overlapping(): def test_markdown_parse_overlapping_escaped(): - frags = tuple(FPDF()._parse_chars("**bold \\__italics\\__**", True)) + frags = merge_fragments(tuple(FPDF()._parse_chars("**bold \\__italics\\__**", True))) expected = (Fragment("bold __italics__", GSTATE_B, k=PDF.k),) assert frags == expected @@ -108,7 +138,7 @@ def test_markdown_parse_crossing_markers(): def test_markdown_parse_crossing_markers_escaped(): - frags = tuple(FPDF()._parse_chars("**bold __and\\** italics__", True)) + frags = merge_fragments(tuple(FPDF()._parse_chars("**bold __and\\** italics__", True))) expected = ( Fragment("bold ", GSTATE_B, k=PDF.k), Fragment("and** italics", GSTATE_BI, k=PDF.k), @@ -126,7 +156,7 @@ def test_markdown_parse_unterminated(): def test_markdown_parse_unterminated_escaped(): - frags = tuple(FPDF()._parse_chars("**bold\\** __italics__", True)) + frags = merge_fragments(tuple(FPDF()._parse_chars("**bold\\** __italics__", True))) expected = ( Fragment("bold** ", GSTATE_B, k=PDF.k), Fragment("italics", GSTATE_BI, k=PDF.k), @@ -153,11 +183,15 @@ def test_markdown_parse_line_of_markers(): def test_markdown_parse_line_of_markers_escaped(): - frags = tuple(FPDF()._parse_chars("\\****BOLD**", True)) - expected = (Fragment("\\****BOLD", GSTATE, k=PDF.k),) + frags = merge_fragments(tuple(FPDF()._parse_chars("\\****BOLD**\\**", True))) + expected = ( + Fragment("**", GSTATE, k=PDF.k), + Fragment("BOLD", GSTATE_B, k=PDF.k), + Fragment("**", GSTATE, k=PDF.k), + ) assert frags == expected - frags = tuple(FPDF()._parse_chars("*\\***BOLD**", True)) - expected = (Fragment("*\\***BOLD", GSTATE, k=PDF.k),) + frags = merge_fragments(tuple(FPDF()._parse_chars("*\\***BOLD**", True))) + expected = (Fragment("****BOLD", GSTATE, k=PDF.k),) assert frags == expected