Selasa, 29 April 2025

PDF Digital Signature using PyHanko with Custom QR Code Image Stamping

For those who are in the middle of searching for a free tools to put digital signature on a PDF file, here are two options that I had ever tried to use :

  • lsnepomuceno/laravel-a1-pdf-sign. It is a plugin for laravel and written in PHP. This library inherits methods from FPDI and FPDI inherits some methods from TCPDF. The documentation is quite easy to understand and there are several examples how to use it to sign PDF file. The drawbacks are, first it does not provide any function to validate the signature and data integrity. Second, this library does not let you to sign the PDF file incrementally. Well, I customized the code so I can check the validity of the signature and the PDF file's integrity. But I still failed to make this library to allow the PDF File being signed incrementally. At this point I stopped trying customizing this library and look for an alternatives with following requirements:
    • it should be an executable script (Go, bash, python, etc...)
    • it should be able put a custom QRcode image for the stamp
  • Python Pyhanko. My search ended with PyHanko. It is an cli, so it matches with my first requirement. But...I was still not sure whether it can satisfy my second requirement. 

I forget to mention, pyhanko is able to generate QRcode as well as the stamp. But I need to put a logo on the QRcode image and unfortunately pyhanko cannot do that. That's why I put "custom" QRcode on this post's title. 

So...after several attempt, asking chatGPT and Gemini I still could not find any solution. And this is the lesson learned....if chatGPT and Gemini can not help you any further, try to read the documentation diligently hehehe....so the solution is right over there.

Again, for those who are looking for Digital Signature tool for PDF that allow you to sign it incrementally and put a custom QRcode for the Stamp.....here are the steps that might help you:

For your information, my server is ubuntu

1. install python (latest version):    

    sudo apt install python3

2. make a virtual environment for python 

    python3 -m venv /path/to/virtual-env

3. activate the virtual environment

    source /path/to/virtual-env/bin/activate

4. install pyhanko latest version (0.26.0) and Pillow 

    pip install pyhanko==0.26.0

     pip install Pillow

5. go to your project directory 

    cd /path/to/your/project/pyhanko

6. prepare the pdf file that you want to sign, the QRcode image and the pkcs12 file (.pfx)

7. I put my passphrase for .pfx file inside a text file. I named it "mypass"

8. make a config file here, for example pyhanko.yml. Put this config inside:

    stamp-styles:
        justqr:
            type: text
            stamp-text: ""
            background:  "/path/to/your/project/pyhanko/myqrcode.png"
            background-opacity: 1

 

7.  sign the PDF file using this command (I used pkcs12, if you use another method you can adjust the signing method)

PYHANKO_CONFIG=pyhanko.yml pyhanko sign addsig \
    --no-strict-syntax \
    --field 1/200,10,300,110/sig1 \
    --style-name justqr \
    pkcs12 \
    --passfile mypass \
    to_sign.pdf \
    signed.pdf \
    mypkcs12.pfx

 

--no-strict-syntax : well...it was a help from chatGPT hehehe...there was an error and this is the solution hehe

--field page/x1,y1,x2,y2/nameOfSigField: --field is to define the location where you want to put the stamp.

    page: the page number

    x1,y1 define the coordinate of the bottom left edge of the stamp 

    x2,y2 define the coordinate of the top right edge of the stamp

    nameOfSigField: it will be used to put the coordinate in byte of the signature. I put sig1 for the first signature, sig2 for the second signature and so on. 

I'm sure that the other options are self explained, right? 

The stamp will looks like this, FYI the QRcode image was generated by another tools

 



Oh if you want to put QRcode and sometext beside the QRcode, for example the name of the signer and the timestamp when it is signed, you can change the config file into this.

stamp-styles:
  qrandtext:
        type: text
        stamp-text: "Signed by \n. %(signer)s on \n%(ts)s"
        background: "/path/to/project/pyhanko/myqrcode.png"
        background-opacity: 1
        background-layout:
          x-align: left
          margins:
            left: 10
            top: 10
            bottom: 10
        inner-content-layout:
          x-align: right
          margins:
            right: 20

 then change the value of --style-name options to "qrandtext" on the cli command:

 PYHANKO_CONFIG=pyhanko.yml pyhanko sign addsig \
    --no-strict-syntax \
    --field 1/200,10,500,110/sig1 \
    --style-name qrandtext \
    pkcs12 \
    --passfile mypass \
    to_sign.pdf \
    signed.pdf \
    mypkcs12.pfx

 

your stamp will look like this


 

From the config file I'm sure you can recognize that I use the background to display the custom QRcode. I set the opacity to 1 so it will be shown as a solid image.  

 I hope this help you somehow.

Kamis, 14 Desember 2023

javascript forEach on json object

 For everyone who is fucked up with forEach on JSON object. 

Find solution in this link

https://codedamn.com/news/javascript/how-to-fix-typeerror-foreach-is-not-a-function-in-javascript

or something like this

 

                        var myval = JSON.parse(results);


                        Object.entries(myval).forEach(entry => {
                            [key, value] = entry;
                            console.log(entry);
                        });

Sabtu, 17 Juni 2023

PHP Timestamp Jakarta

Just for my personal purpose to remind me something easy but easily forgettable as well. Here is how we get timestamp for jakarta.

ini_set('date.timezone', 'Asia/Jakarta');
$ts = date('Y-m-d H:i:s');

Jumat, 04 Februari 2022

Datatable: set title to downloaded File

 Short post, in case I need it later. 

So my problem was, I have several datatables in one page and each datatable has download buttons. Normaly, datatable will use the page's title as the name of the downloaded file. But I have more than one datatables and I want the downloaded file has different name. 

And here is the solution.


$('#zero_configuration_table').DataTable( {
	dom: 'Bfrtip',
	buttons: [
		{extend:'excelHtml5', title: 'Dashboard Tracer Study'},
        {extend:'pdfHtml5', title: 'PDF Dashboard Tracer Study'}
	],
	scrollX:        true,
	scrollCollapse: true,
	paging:         false,
	fixedColumns: false,
	autoWidth: false,
});

Take a look at the buttons, simple right. I can even put different name on each button. With that code, the datatables doesn't do pagination and it is horizontally scrollable.

Datatable : Adding a Row Dynamically and adding its atrribute

Ok, I had a problem to solve the problem I mentioned in the title of this post. 

So, I combined two solution from two sources (Actually I've browsed to several websites and discussions). 

These are my sources: Source 1, Source 2.

I use ajax to retrieve the data and then add them dynamically to the table. Then, I want to align the text to center and add onclick event on each row.

So here is my Table:


<div class="table-responsive">
	<table class="table table-striped table-bordered" 
    	id="transaction_detail" cellspacing="0" width="100%">
		<thead>
			<tr class="text-center align-middle">
				<th>Name</th>
				<th>email</th>
				<th>Phone</th>
				<th>Transactions</th>
				<th>Turnover</th>

			</tr>
		</thead>
		<tbody >

				
		</tbody>
	</table>
</div>


And here is my Ajax:


$.ajax({
  type:"POST",
  url:"/gettransaction",
  data:{customer_id: customerid},
  success:function(data){
	var jsondat = JSON.parse(data)  ;
	
	//clear the table's body
	$("#transaction_detail").DataTable().clear().draw();

	if(jsondat.length > 0){
		for(var i=0; i<jsondat.length; i++){
			
			var rowTab = $("#transaction_detail")
				.DataTable()
				.row
				.add([
				jsondat[i].customer_name,
				jsondat[i].customer_address,
				jsondat[i].customer_phone,
				jsondat[i].number_transaction,
				jsondat[i].turnover
			]);


			//use node() so that row is editable.
			var rowEdit = rowTab.node();

			$(rowEdit).attr("style", 
				"cursor:pointer; text-align:center");
			$(rowEdit).attr('onclick',
            	"showTrans("+jsondat[i].customer_id+")");

			//redraw the table after its modified
			rowTab.draw(false);
		}   

	}
  },
  error: function(){

  }
});


I hope it can help you somehow. cheers!!

Sabtu, 15 Januari 2022

datatable: show row per page and export button at the same time

Well this posting is an extra info of my previous posting. After I struggled with an error, now I want to show the page per button and the export button at the same time. 

I found two ways from stackoverflow

so here is the first solution:


$(document).ready(function() {
    document.title = 'page Title';
    $('#example').DataTable( {
        dom: 'Bfrtip',
        lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "All"]],
        buttons: [
            'pageLength',
            'copyHtml5',
            'excelHtml5',
            'csvHtml5',
            'pdfHtml5'
        ],
    } );
} );

You can delete or comment the second line, it was included in my code since I coded it in a team and the used template seems doesn't have title tag in its header. So, I set it in here. The title of the page will be used as the filename when its downloaded. 

The second solution is:


$(document).ready(function() {
    document.title = 'page Title';
    $('#example').DataTable( {
        dom: 'lBfrtip',
        lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "All"]],
        buttons: [
            'copyHtml5',
            'excelHtml5',
            'csvHtml5',
            'pdfHtml5'
        ],
    } );
} );

Take a look at the 'dom:' attribute. The second solution uses 'lBftrip' whereas the first solution uses 'Bfstrip'. now then take a look closer to the first solution, if we use 'Bfstrip', then we have to add 'pagelength' as a button as well. 

Just try them and see the different. NOTE!!! don't forget to call all the needed css and js scripts. You can find them all here.