end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

pypdf for python で pdf 内に閲覧を禁止するannotationを追加し、閲覧期限内の場合、pdfのjavascriptでannotationを非表示化

以下のpython scriptで、それっぽいpdfを作成することができます。

どうやら、google chromeの場合、pdfのjavascriptがうまく動作せず、 実用はできないと思います

from pypdf             import PdfReader, PdfWriter
from pypdf.generic     import NameObject, create_string_object, DictionaryObject
from pypdf.annotations import Rectangle
from reportlab.pdfgen  import canvas
import io
import sys

new_pdf_path   = "new.pdf"
owner_password = "ownerpassword"

js_code = """
var expireDays = 10;
var createDate = this.info.CreationDate;
// new Date()は google chromeのpdf.jsでは動作しないみたい
var nowDate    = new Date();

var diffDays   = Math.floor((nowDate-createDate) / (1000*60*60*24));

// 閲覧期限内の場合、マスクとして使用しているアノテーションを非表示化
if( diffDays <= expireDays ) {
    var annots = this.getAnnots();
    for (var i = 0; i < annots.length; i++){
        //app.alert( annots[i].type );
        if( annots[i].type == "Square"){
            annots[i].hidden = true;
        }
    }
} else {
    this.pageNum = 0;
    app.alert( "このPDFは期限切れです" );
    this.closeDoc();
}
"""

def main():
    org_pdf_path = sys.argv[1]
    
    pdf_reader = PdfReader( open(org_pdf_path, "rb") )
    pdf_writer = PdfWriter()
    pdf_writer.add_metadata( pdf_reader.metadata )

    pageNum = 0
    for page in pdf_reader.pages:
        page = add_javascript_to_page(page)
        # page = add_rectangle_to_page(page)
        pdf_writer.add_page(page)
        add_annotation_to_page(pdf_writer,page,pageNum)
        
        pageNum+=1

    encrypt_pdf(pdf_writer)
    
    with open(new_pdf_path, "wb") as output_file:
        pdf_writer.write(output_file)
        
def encrypt_pdf(pdf_writer):
    # https://pypdf.readthedocs.io/en/stable/modules/PdfWriter.html
    # 3(0x8):print, 9(0x200):form field, 10(0x400):text extract
    # 5,6(0x20,0x40): annotation
    # ユーザによるannotation編集を許可しないと
    # jsでの「annots[i].hidden = true」が動作しないって、意味がないのでは?
    permissions_flag = 0x8+0x20+0x40+0x200+0x400
    pdf_writer.encrypt(user_password    ="",
                       owner_password   =owner_password,
                       use_128bit       =True,
                       permissions_flag =permissions_flag
                       )
    
def add_javascript_to_page(page):
    js_obj = DictionaryObject()
    js_obj.update({ NameObject("/JS"): create_string_object(js_code),
                    NameObject("/S"): NameObject("/JavaScript") })
    o_obj = DictionaryObject()
    o_obj.update({ NameObject("/O"): js_obj})
    page[NameObject('/AA')] = o_obj
    return page
    
def add_annotation_to_page(pdf_writer,page,pageNum):
    # https://pypdf.readthedocs.io/en/stable/user/adding-pdf-annotations.html
    rect_coords = (20, 20,
                   page.mediabox.width-20, page.mediabox.height-20 )
    annotation = Rectangle( rect=rect_coords,
                            interiour_color="aaaaaa" )
    pdf_writer.add_annotation(page_number=pageNum,
                              annotation=annotation)

def add_rectangle_to_page(page):
    packet = io.BytesIO()
    can = canvas.Canvas(packet,
                        pagesize=(page.mediabox.width,
                                  page.mediabox.height))
    # alpha=0に近い程,透明
    can.setFillColorRGB(0.5, 0.5, 0.5, alpha=0.95)
    can.rect(20,20,                     # 左下座標
             page.mediabox.width-40,    # 幅
             page.mediabox.height-40,   # 高
             fill=True)
    can.save()

    # move to the beginning of the StringIO buffer
    packet.seek(0)
    new_pdf = PdfReader(packet)

    # merge the rectangle with the page
    page.merge_page(new_pdf.pages[0] )
    return page

if __name__ == "__main__":
    main()