1

This question is following this one. I am signing PDF programmatically. Every new signature is added in an incremental way where I add the signature dictionnary after the %EOF. Everything works fine with one signature, I get the green tick and my signature is valid as you can see in the previous question. My problem comes when I try to sign a second time, I get these errors :

enter image description here

What I don't understand is that my way of computing the byte range hasn't changed and works fine with 1 signature. Am I missing something ? Here is my file as a string since I can't use any file uploader because of my proxy. Tell my if you need more information, thanks.

%PDF-1.3
%âãÏÓ

1 0 obj
<<
/Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R
>>
endobj

2 0 obj
<<
/Type /Outlines
/Count 0
>>
endobj

3 0 obj
<<
/Type /Pages
/Count 2
/Kids [ 4 0 R 6 0 R ] 
>>
endobj

4 0 obj
<<
/Type /Page
/Parent 3 0 R
/Resources <<
/Font <<
/F1 9 0 R 
>>
/ProcSet 8 0 R
>>
/MediaBox [0 0 612.0000 792.0000]
/Contents 5 0 R
>>
endobj

5 0 obj
<< /Length 1074 >>
stream
2 J
BT
0 0 0 rg
/F1 0027 Tf
57.3750 722.2800 Td
( A Simple PDF File ) Tj
ET
BT
/F1 0010 Tf
69.2500 688.6080 Td
( This is a small demonstration .pdf file - ) Tj
ET
BT
/F1 0010 Tf
69.2500 664.7040 Td
( just for use in the Virtual Mechanics tutorials. More text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 652.7520 Td
( text. And more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 628.8480 Td
( And more text. And more text. And more text. And more text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 616.8960 Td
( text. And more text. Boring, zzzzz. And more text. And more text. And ) Tj
ET
BT
/F1 0010 Tf
69.2500 604.9440 Td
( more text. And more text. And more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 592.9920 Td
( And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 569.0880 Td
( And more text. And more text. And more text. And more text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 557.1360 Td
( text. And more text. And more text. Even more. Continued on page 2 ...) Tj
ET
endstream
endobj

6 0 obj
<<
/Type /Page
/Parent 3 0 R
/Resources <<
/Font <<
/F1 9 0 R 
>>
/ProcSet 8 0 R
>>
/MediaBox [0 0 612.0000 792.0000]
/Contents 7 0 R
>>
endobj

7 0 obj
<< /Length 676 >>
stream
2 J
BT
0 0 0 rg
/F1 0027 Tf
57.3750 722.2800 Td
( Simple PDF File 2 ) Tj
ET
BT
/F1 0010 Tf
69.2500 688.6080 Td
( ...continued from page 1. Yet more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 676.6560 Td
( And more text. And more text. And more text. And more text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 664.7040 Td
( text. Oh, how boring typing this stuff. But not as boring as watching ) Tj
ET
BT
/F1 0010 Tf
69.2500 652.7520 Td
( paint dry. And more text. And more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 640.8000 Td
( Boring.  More, a little more text. The end, and just as well. ) Tj
ET
endstream
endobj

8 0 obj
[/PDF /Text]
endobj

9 0 obj
<<
/Type /Font
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Encoding /WinAnsiEncoding
>>
endobj

10 0 obj
<<
/Creator (Rave \(http://www.nevrona.com/rave\))
/Producer (Nevrona Designs)
/CreationDate (D:20060301072826)
>>
endobj

xref
0 11
0000000000 65535 f
0000000019 00000 n
0000000093 00000 n
0000000147 00000 n
0000000222 00000 n
0000000390 00000 n
0000001522 00000 n
0000001690 00000 n
0000002423 00000 n
0000002456 00000 n
0000002574 00000 n

trailer
<<
/Size 11
/Root 1 0 R
/Info 10 0 R
>>

startxref
2714
%%EOF
1 0 obj 
<</Type /Catalog /Outlines 2 0 R /Pages 3 0 R /AcroForm <</Fields [11 0 R ] /SigFlags 3 >> /Names 14 0 R >>
endobj
3 0 obj 
<</Type /Pages /Count 2 /Kids [4 0 R 6 0 R ] >>
endobj
4 0 obj 
<</Type /Page /Parent 3 0 R /Resources <</Font <</F1 9 0 R >> /ProcSet 8 0 R >> /MediaBox [0 0 612 792 ] /Contents 5 0 R >>
endobj
6 0 obj 
<</Type /Page /Parent 3 0 R /Resources <</Font <</F1 9 0 R >> /ProcSet 8 0 R >> /MediaBox [0 0 612 792 ] /Contents 7 0 R >>
endobj
11 0 obj 
<</Type /Annot /SubType /Widget /Rect [0 0 0 0 ] /P 4 0 R /F 4 /FT /Sig /T (Signature1) /Ff 0 /V <</Type /Sig /Filter /Adobe.PPKLite /SubFilter /adbe.pkcs7.detached /ByteRange [0 3730 15474 422                   ] /Contents <3082092b06092a864886f70d010702a082091c30820918020101310f300d06096086480165030402010500300b06092a864886f70d010701a08205cf308205cb308203b3a003020102020900d0ffeb9c9f715144300d06092a864886f70d0101050500307c310b30090603550406130246523111300f06035504080c084252455441474e45310f300d06035504070c0652454e4e4553310d300b060355040a0c0441544f53310b3009060355040b0c0242533110300e06035504030c07746573742e6672311b301906092a864886f70d010901160c7465737440746573742e6672301e170d3137303731383037343433355a170d3138303731383037343433355a307c310b30090603550406130246523111300f06035504080c084252455441474e45310f300d06035504070c0652454e4e4553310d300b060355040a0c0441544f53310b3009060355040b0c0242533110300e06035504030c07746573742e6672311b301906092a864886f70d010901160c7465737440746573742e667230820222300d06092a864886f70d01010105000382020f003082020a0282020100df7b0bfe8369008eed0719fd59b90780746a4a15058985e99559f0f5bb83cd4fdfa53ca43a797f39b9e308561fde0e48924af78399f50c3ef1793088f941ef3f0eeb93b2a2e0615a0164f3c979c96c0a703d4e4f475b27b6c21f7cce37b9cdad8098e8de26f2afc69e50e7153f8928cbd2f504b3b1ec5c082b9175d92074849330479f2db361b522e144795e59a65cc92b48d2a0237b3515315d432d951746bdcf872d32ad3b2e454813051bde3544e053c9098b3d428bd783c3a527650b69a419a0f262643b93d3e24b0ecec65d9ced1ac16879079794de9c46edc1b5675f400fd596e984379eafe115847d6f52c581b4a5c08dcb271f1b264578e805d53bae54fc3bcde0a82675a49759e662a3b43796911f3c6e76030be5099508054297c2403b8a5b0c4c27c096efaf1335495d99807aa6b191ddbe3719c93f0943995a8a4246fbb98b1279883df625c3823650f70130076c5cb444b0f153652304b6cb48ce93c969b2ece9f0073a992a7d2a089feb5386c7af7ff0bb7e073dfa485d2df24e79c2e8b4ef6e68123b2be8befbd2f01130438408dad3b6a11843fcf794bda25f3764dd8b2900202d308ff4aff2dd34b04cd39cfc3a2357a7e30eaac4afc520488b05f9405538405d30c61418f14e8a2ed55a55b4e32d59ef41846dae7fd163786fd41966bc7e5a4e03d064a793f301f74fa4717885332daf35dbda46889d2d0203010001a350304e301d0603551d0e0416041428518f33e55c29ed1dbfb4a0a54d8732cc9013a5301f0603551d2304183016801428518f33e55c29ed1dbfb4a0a54d8732cc9013a5300c0603551d13040530030101ff300d06092a864886f70d0101050500038202010008207d9828c97c982a2ac85517ef1e56bfa14174c4e0a95611057237cb1a134a1966709f42e45c1b9487c5cb2b34e1b8cd2ddae91f8eb2b53e7ee10488ab483162899c1035c287a2183bc7c031d99e21ebf4cbd9f116928ca1fa65295dbddbe13a77059cc8cb3c32ec8d61c2c2baa2ae3ada698843f7dd7b9a58abf1ce50f926bdc09b2da423ecaca667d47e93fa85372acadc4f45b1343cdf6298dfcc732c76a94fbaab051ae8de3ff45fad16be3c020c2449428c36eaeb25b4c3ae4cd8055f3210ecf58de9928635549182490d1cbf9765bbe2c903c6648b9d9621290a37aff1e9e5509fbf866d60d0f0796f2c9d896ba9cc3be76aba99e780af83f8c96667f2e1122957ed9df0a1406c9466b383c3c9528788805e8ebfd5003e29d9e8250c18b22ae1bb86a98436d9216f5d4f9e0e339bf883a71c7b16abddc65cead101d5161458b22f4978a39be42d85776c96e86fa3f942115793e7d539e5f03d7358d2869a601afbafce454d8122f23fadef3b162d375746c38e64785e4d26760b3fceec604f04afd27470c56f31695b457cd8f4caf1dcf9fe783b377567f1584f93c87e24277af0c265e666ecc91a2dd15561dbaa6f4d2eb293621abf1026edc093706f45dfc1e3dd6915d51171f1353b49802cfc224dd8d5a574dc870fd05502fdd8ba2d520cfbbe8cb30a7a7935bf530c14fd8d9964de127255714b6baf7a88cf86318203203082031c020101308189307c310b30090603550406130246523111300f06035504080c084252455441474e45310f300d06035504070c0652454e4e4553310d300b060355040a0c0441544f53310b3009060355040b0c0242533110300e06035504030c07746573742e6672311b301906092a864886f70d010901160c7465737440746573742e6672020900d0ffeb9c9f715144300d06096086480165030402010500a069301806092a864886f70d010903310b06092a864886f70d010701302f06092a864886f70d010904312204205cdb266772f8c738f3038331b7da5f43925cf5d3611a271522ed23ef56632b8b301c06092a864886f70d010905310f170d3137303830323039353633315a300d06092a864886f70d01010b050004820200c8b114cb6d49ef4dbaf66393f422f501f5cbaa94bc5fa9c57055ec11cc61ae3255b19e51cc2dbdad973659f0c5a05d42214e21848acf7a7429d29cf3d405c78a06bd68f32a0d70e1c58250f70e8b21c05abda323dee4967d95c0743f83037eb7a42836f61d6710712a215f8446ed1eb6c15d78b952aef089674249fa591612f4085b2870488c3c8634800d907887348741400ba0a65e45a9bf81a0529f727ddccf5650fc4807c67b3561248c3655770be780ddd8ea8ae372d1925ab2d58dd7d93f3b10259ef15810b76b41ed390c8b297c913f811ffd2d19584e9a846499ad6e4f62058cad23baecad34202b2706f2c41dfa7e33042d08868e09d53a5e3ef59c0a7b3023ab5d64e8e56ebe8f1b885fe28db00b23d35c9970054ec5ebc3de66d7a348f718d62dc5e1743ee735cf7608b4b2ed5a7608528f89b52bb7f56f75e78c0d63d05bfd74fca5dea12e1f15d80d2a6d1e3219ea503455b4ac8248f5071a92244fa828d92b297401fd235ad21024c3460a700a60219d5034c82fdefab8c1871c75901b8df7839ce5dee20552110865087c48849c857fd596c58270bd1e8e3bc33918837e28eefcdb1c6fea6f531cd5ee8b29ad4a89ddb098d8a0e1ad1e5fd84f725f5a2cdbc4d722353c593d8c2af73310162b50dff35252261d936d8d33de6840a61827237335966ed19d44d4ebb39a4b0a64ab609d68ab781aa84275d73c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000> 
/M (D:20170802115620+02'00') >> >>
endobj
12 0 obj 
[]
endobj
13 0 obj 
<</Names 12 0 R >>
endobj
14 0 obj 
<</Dests 13 0 R >>
endobj
xref
0 2 
0000000000 65535 f 
0000003028 00000 n 
3 2 
0000003152 00000 n 
0000003216 00000 n 
6 1 
0000003356 00000 n 
11 4 
0000003496 00000 n 
0000015518 00000 n 
0000015538 00000 n 
0000015574 00000 n 
trailer
<</Size 15 /Root 1 0 R /Info 10 0 R /Prev 2714 >>
startxref
15610
%%EOF
1 0 obj 
<</Type /Catalog /Outlines 2 0 R /Pages 3 0 R /AcroForm <</Fields [11 0 R 15 0 R ] /SigFlags 3 >> /Names 14 0 R >>
endobj
3 0 obj 
<</Type /Pages /Count 2 /Kids [4 0 R 6 0 R ] >>
endobj
4 0 obj 
<</Type /Page /Parent 3 0 R /Resources <</Font <</F1 9 0 R >> /ProcSet 8 0 R >> /MediaBox [0 0 612 792 ] /Contents 5 0 R >>
endobj
6 0 obj 
<</Type /Page /Parent 3 0 R /Resources <</Font <</F1 9 0 R >> /ProcSet 8 0 R >> /MediaBox [0 0 612 792 ] /Contents 7 0 R >>
endobj
14 0 obj 
<</Dests 17 0 R >>
endobj
15 0 obj 
<</Type /Annot /SubType /Widget /Rect [0 0 0 0 ] /P 4 0 R /F 4 /FT /Sig /T (Signature2) /Ff 0 /V <</Type /Sig /Filter /Adobe.PPKLite /SubFilter /adbe.pkcs7.detached /ByteRange [0 16641 28385 387                  ] /Contents <308209...> 
/M (D:20170807115851+02'00') >> >>
endobj
16 0 obj 
[]
endobj
17 0 obj 
<</Names 16 0 R >>
endobj
xref
0 2 
0000000000 65535 f 
0000015896 00000 n 
3 2 
0000016020 00000 n 
0000016084 00000 n 
6 1 
0000016224 00000 n 
14 4 
0000016364 00000 n 
0000016400 00000 n 
0000028422 00000 n 
0000028442 00000 n 
trailer
<</Size 18 /Root 1 0 R /Info 10 0 R /Prev 15610 >>
startxref
28478
%%EOF

I put "..." in the second signature because of the character limitation of Stack Overflow but it is the same length as the first one.

EDIT : Here is the link to the first signed PDF and here is the link to the second that is signed twice.

Shashimee
  • 256
  • 3
  • 20
  • if you send me both PDFs (first signed and second signed) i could take a closer look – PatrickF Aug 07 '17 at 10:56
  • @Patrick I tried in the course of the op's previous question to make clear that a binary copy is required. The op has not provided one stating problems sharing PDFs from his job. – mkl Aug 07 '17 at 16:17
  • Well I can send you the two files by mail but all of the file hosting sites are blocked :/ – Shashimee Aug 08 '17 at 07:11
  • @PatrickFritzsch I updated the question to include the links. – Shashimee Aug 08 '17 at 14:16
  • @mkl I now shared the PDF links. Could you take a look at it when you have time ? You would save the say :) – Shashimee Aug 09 '17 at 09:01

1 Answers1

2

In your latest cross reference table

xref
0 2 
0000000000 65535 f 
0000015896 00000 n 
3 2 
0000016020 00000 n 
0000016084 00000 n 
6 1 
0000016224 00000 n 
14 4 
0000016364 00000 n 
0000016400 00000 n 
0000028422 00000 n 
0000028442 00000 n 

all offsets of used objects except of object 1 are too low by 7. Your reference to the cross reference table

startxref
28478

also is too low by 7.

Such cross reference errors make Adobe Reader "repair" the file while loading which in particular causes existing objects to be moved etc.

Moving objects in the file obviously will make the formerly correct signed byte range offsets invalid.

I repaired the end of your file here to

xref
0 2 
0000000000 65535 f 
0000015896 00000 n 
3 2 
0000016027 00000 n 
0000016091 00000 n 
6 1 
0000016231 00000 n 
14 4 
0000016371 00000 n 
0000016407 00000 n 
0000028429 00000 n 
0000028449 00000 n 
trailer
<</Size 18 /Root 1 0 R /Info 10 0 R /Prev 15610 >>
startxref
28485
%%EOF 

and now the first signature is reported as valid and the second one as invalid due to alterations or corruption after signing - which is correct, after all I changed something - but not due to byte range issues:

Screenshot of signature panel

PS: Without the binary original files such issues hardly can be found as offsets in your pasted PDF internals are incorrect anyways and one would have to try and identify different kinds of incorrectness...

mkl
  • 90,588
  • 15
  • 125
  • 265
  • Awesome ! I wouldn't have find that, I know exactly where it comes from then. You really saved me :) – Shashimee Aug 09 '17 at 09:32
  • If you are up for some more PDF action I posted another question haha : https://stackoverflow.com/questions/45584622/add-digital-signature-appearance-programmatically – Shashimee Aug 09 '17 at 09:54
  • An additional issue is that the Widget annotations are not listed in the Annot array (not defined at all) of the pages. – Jan Slabon Aug 09 '17 at 11:00
  • @Jan it's invisible anyways. Thus, strictly speaking no need to refer to from the **Annots** of any page as long as the signature field is reachable from the **AcroForm** dictionary. – mkl Aug 09 '17 at 13:48
  • @Shashimee *"If you are up for some more PDF action I posted another question haha"* - unfortunately I'm not into PHP at all. Thus, all I could do is copy required objects from some existing signed PDF. – mkl Aug 09 '17 at 14:21
  • @mkl Technical no need, but as the (invisible) widget annotation refers to the page through its P entry the other way should be provided, too. IMHO it should be good practice to assign the widget annotation to an Annots array too instead of letting the annotation floating arround. – Jan Slabon Aug 10 '17 at 08:01
  • @Jan Right, whenever am annotation references a page, the page really should have that annotation in its **Annots**. The op actually doesn't need annotations at all for invisible form fields, so he could remove the merged annotation from the form field. – mkl Aug 10 '17 at 09:44
  • @mkl ok, I newer saw such structure in the wild. BTW: There's also a type in the field dictionary: SubType vs. Subtype – Jan Slabon Aug 10 '17 at 11:22
  • FYI, the error OP has can also happen if an XREF entry doesn't end with a two-character EOF (I only had \n, setting space + \n worked!). Maybe it can help someone later ;-) – wormsparty Mar 17 '21 at 08:00
  • Indeed, there is a lot one can do wrong. Incorrect EOLs (resulting in incorrect entry sizes) indeed often are found (see [here](https://stackoverflow.com/a/55438597/1729265) for the most relevant rules in that regard) but there are many other details which one can also do wrong, too many for a single answer to enumerate – mkl Mar 17 '21 at 11:18