From b1b9e09e2586ec3c3d7e843306b6506c591b5364 Mon Sep 17 00:00:00 2001 From: Antonov548 Date: Thu, 4 Jul 2024 18:03:21 +0200 Subject: [PATCH] add initial `sign` function --- npm/podofo.d.ts | 1 + src/bind.cpp | 37 ++++++++++++++++++++++++++++++++ tests/test_data/certificate.der | Bin 0 -> 879 bytes tests/test_data/private_key.der | Bin 0 -> 1218 bytes tests/unit_tests/sign.test.js | 34 +++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+) create mode 100644 tests/test_data/certificate.der create mode 100644 tests/test_data/private_key.der create mode 100644 tests/unit_tests/sign.test.js diff --git a/npm/podofo.d.ts b/npm/podofo.d.ts index a658f5f..f92a5ac 100644 --- a/npm/podofo.d.ts +++ b/npm/podofo.d.ts @@ -22,6 +22,7 @@ declare module 'podofo.js' { export interface Page extends Canvas { getRect: () => Array; extractText: () => Array<{text: string}>; + sign: (image: Image, rect: Array, certificate: Uint8Array, privateKey: Uint8Array) => Uint8ClampedArray; } export interface PageCollection { diff --git a/src/bind.cpp b/src/bind.cpp index c81a8ac..24ce04e 100644 --- a/src/bind.cpp +++ b/src/bind.cpp @@ -256,6 +256,42 @@ em::val getPageSize(const PoDoFo::PdfPageSize& page_size, PoDoFo::PdfPage::CreateStandardPageSize(page_size, landscape)}; return rectToArray(size); } + +em::val sign(PoDoFo::PdfPage& page, const PoDoFo::PdfImage& image, em::val jrect, em::val certificate, em::val key) +{ + const auto& rect{vecFromJSArray(jrect)}; + + const auto certificate_buffer{vecFromJSArray(certificate)}; + const auto key_buffer{vecFromJSArray(key)}; + + auto& signature = page.CreateField("Signature", PoDoFo::Rect(rect[0], rect[1], rect[2], rect[3])); + signature.SetSignatureDate(PoDoFo::PdfDate::LocalNow()); + + auto form{page.GetDocument().CreateXObjectForm(PoDoFo::Rect(0, 0, image.GetWidth(), image.GetHeight()))}; + + PoDoFo::PdfPainter painter; + painter.SetCanvas(*form); + painter.DrawImage(image, 0, 0, 1, 1); + painter.FinishDrawing(); + + std::vector buffer; + PoDoFo::VectorStreamDevice output{buffer}; + + PoDoFo::PdfSignerCms signer( + {reinterpret_cast(certificate_buffer.data()), certificate_buffer.size()}, + {reinterpret_cast(key_buffer.data()), key_buffer.size()} + ); + + signature.SetAppearanceStream(*form); + + PoDoFo::SignDocument(static_cast(page.GetDocument()), output, signer, signature, PoDoFo::PdfSaveOptions::SaveOnSigning); + + const auto Uint8ClampedArray{em::val::global("Uint8ClampedArray")}; + + return Uint8ClampedArray.new_(em::typed_memory_view( + buffer.size(), reinterpret_cast(buffer.data()))); +} + } // namespace Page namespace Resources @@ -309,6 +345,7 @@ EMSCRIPTEN_BINDINGS(PODOFO) .function("getRect", &Page::getRect) .function("extractText", &Page::extractText) .function("getResources", &Page::getResources, em::allow_raw_pointers()) + .function("sign", &Page::sign) ; em::class_("Canvas") diff --git a/tests/test_data/certificate.der b/tests/test_data/certificate.der new file mode 100644 index 0000000000000000000000000000000000000000..9ac18186765e1fc878086fe42febfc307b01a4a3 GIT binary patch literal 879 zcmXqLV$L>bVhUcs%*4pVB$8*n?Q?Bhz}wIFwX1rSc5m2yLT!!#FB_*;n@8JsUPeZ4 zRt5uCLv903Hs(+kHen_gS3_X~K@f+7hbuTgH&r*dB(WsbP|-jhB*?`h;h9>9lVs zq7a^$lAc*otPoIAso+zRVjw5ZYh+?zZeU_)YG`b183p8;B5|o@O%tOMvIiJh8JL?G z`56qF7`d357#SIM{kt)%cYo2IO| zc~9y7e3dsp`9%xEc3F9Ea|r)DTf_bv_n#Gh%50XZvD<2TY98NYbvY%`BIQ0|x20r@ zteIRnqip-u3BPYe>pSJ2@71+@;&+|vw0YdxzBLbvPCl(vpYp~>@3+7Djb%a4SMFqM z@Hr+Fb8XL@iObRj|NG7Pz1#AaVru2J6}#>v-+X&~?v)?uw>Ue6E$obrFiQ7c`og|_ z!@<*j7anW>QcbVkA1Tb!#>KOnZSj+n_kI;0d+DGRpsXOmGb32~sav)RR!(b*S-PP3?wpAwpY?hgeGcoWZ?{VmxBJBY z|EEo2me;F^CO7I9-mJcR_pe=1+uUvLqK|quHi-w%=KYcseX8+!eDI3Ugd1$Les^AM zuYTlir+1?Dy^!r%-hJiE)pGJ5e?kK2un&L}?lt9~HB J@VjbaJpk!TYGeQa literal 0 HcmV?d00001 diff --git a/tests/test_data/private_key.der b/tests/test_data/private_key.der new file mode 100644 index 0000000000000000000000000000000000000000..0575d511e6bd5f083775333a568424fda0509210 GIT binary patch literal 1218 zcmV;z1U>sOf&{(-0RS)!1_>&LNQUrs4#*Aqyhl|0)hbn0J{FznvIZ7 ze_!}i(p-4m(eLtLGxv)KAQq96XJ>oZ8Jl-sf|RJ)-Xh(O&I!m@dSM!@F zKlTg!s7@mWIVD}Te2jeK*#$((7=#)}kh?hcL5!TwUGPTTQ5Ry&y6iPn(`lrc&prxdwnt#u8lCyquAyXA%ESocp^u`XOa`)~LGN zW7+M;oz(be+6jphI6gAO0UM3d^ar=F!OKq2<16|lXM4X{6AXq547&!S--+WNyAdQb=pDuD z73BZuTH4-c;Mt_8&&PMNfe>7=VCyoPho+}Uk~j!h7tTHtm16K*se)u1X$sOShm51z zA9>h!pV*09D~5zY5dzHlaJzkm|Ng1$_b_;$(NX-?`>5e7 z4gFLf+|Sb`VpJw4dsYD~Nq*Bgl?cdm+(34|BntYM0=78{?&8p>ogmOx1$zR4fdIiM zZdKHQx?}CdLiLINFwJPpHQ$3p*qq5P?6-ZBbDMI5yEeGwD_?tz{N&Z1Sjwtn^um;| z*Bv`>#ZChKC)lWXBc$531u@2{jAEFkfDqGsiQUllou%@*<|Lfdgc}~)Oe>W9I+dhj zW~3DHrGJ9N$z&6b>M~q%-G%9H>pud4fdKc_DZ(7)0jSgWemGZ*FhBB*d%8>8-qd#z zCbR8Tv=ghTNp|S3zf;9&lyB2=rI(%(%;y|tN+tpoOCPoxKNw$)iw=dbq;{(Rmm4TV zYep)Qy#iPh_Nv^zq)@xtSpL;9DZZ)VYef7*NL@$5@{cB8uJ9O|n(Q9>}>{5oSBaANIv(j`O~T zTKhn`UWv0vMNgAEW6Ymhd}vj9JTJ9AL`FBCiaHJ-4iZg3xlqv}p2A+qyBtW}&4CI { + it('sign pdf document', async () => { + const Podofo = global.Podofo; + + const document = new Podofo.Document(); + const pages = document.getPages(); + + const size = Podofo.getPageSize(Podofo.PageSize.A4, false); + const page = pages.createPage(size); + + const fonts = document.getFonts(); + const font = fonts.getDefaultFont(); + + const painter = new Podofo.Painter(); + painter.setCanvas(page); + painter.setFont(font, 10); + + const text = 'Signed document'; + painter.drawText(text, 56.69, page.getRect()[3] - 56.69); + painter.finishDrawing(); + + const buffer = readFile('image.png'); + + const image = new Podofo.Image(document); + image.loadFromBuffer(buffer); + + const certificate = readFile('certificate.der'); + const key = readFile('private_key.der'); + + page.sign(image, [20, 20, 100, 100], certificate, key); + }); +});