PythonでOutlookから選択したメールの情報を取得し、PDFポートフォリオ(コレクション:以下、PDFポートフォリオに表現を統一)を作成するスクリプトを作成した時の記録です。PDFポートフォリオは大変便利ですが、日本語の情報が見当たらないので紹介します。
作成環境
- Python環境
Pythonの環境はAnacondaを利用しました。本記事作成時に利用したのは64-Bit Graphical Installer (457 MB)のPython 3.8です。下記URLのページよりダウンロードしてください。
Anaconda:https://www.anaconda.com/products/individual
- 必要なライブラリの導入
Anaconda Prompt (Anaconda3)で以下を実行します。PyMuPDFとfizの導入順によってはエラーが出るので注意です。なお、利用したAnacondaはpywin32ライブリが初めから導入されていましたが、もし素のPythonを利用する方は「pip install pywin32」とAnaconda Prompt (Anaconda3)に入力して導入してください。
pip install PyMuPDF pip install fiz pip install fonttools
Outlookからの情報取得
OutlookはOffice365のバージョン2104で確認しています。例として下記画像メールの件名、受信日、本文、差出人、宛先(To)、写し(Cc)の情報を取得します。
- 情報取得のスクリプト
Outlookから情報を取得するためにwin32com.clientライブラリを利用します。本ライブラリはエクセルなどの情報取得にも利用することができます。なお、選択メールの情報のSubjectなどを指定することで取り出すことができます。
import win32com.client from datetime import datetime #Outlook処理 outlook = win32com.client.Dispatch('Outlook.Application') #選択メールの情報を取得 messages = outlook.ActiveExplorer().Selection #print(type(messages)) for cnt, message in enumerate(messages): #受信日 ItemDate = message.ReceivedTime ItemDate = ItemDate.strftime("%Y-%m-%d %H:%M:%S") print('受信日_選択メール%d:%s' %(cnt+1, ItemDate)) #件名 ItemSubject = str(message.Subject) print('件名_選択メール%d:%s' %(cnt+1, ItemSubject)) #本文 ItemMessage = str(message.Body) print('本文_選択メール%d:%s' %(cnt+1, ItemMessage)) #差出人(From) ItemFrom = str(message.SenderEmailAddress) print('差出人_選択メール%d:%s' %(cnt+1, ItemFrom)) #宛先(To) ItemTo = str(message.To) print('宛先_選択メール%d:%s' %(cnt+1, ItemFrom)) #写し(Cc) ItemCc = str(message.CC) print('Cc_選択メール%d:%s' %(cnt+1, ItemCc)) #添付ファイル ItemAttachements = message.attachments #添付ファイル数 ItemAttachementsCnt = message.attachments.count print('添付ファイル数_選択メール%d:%s' %(cnt+1, ItemAttachementsCnt))
実行結果
受信日_選択メール1:2021-05-05 12:18:30 件名_選択メール1:ブログテストメール 本文_選択メール1: Pythonは楽しいな。でも、Rも良いもんだ。 差出人_選択メール1:info@karada-good.net 宛先_選択メール1:info@karada-good.net Cc_選択メール1:info@karada-good.net 添付ファイル数_選択メール1:1
PDFポートフォリオの構造
PDFポートフォリオの作成ですが、PDFを構成する最上位の文書カタログ辞書の設定です。文書カタログ内に新たに「Collection」を追記し、Collection/Type、Collection/Schema、Name/Names/EmbeddedFiles/Names、Collection/Splitなどを設定することでPDFポートフォリオを作成することができます。なお、PDF内のフォームやファイル名はencodeコマンドなどで「utf-16BE」(utf-16のビックエンディアン)に変換後、先頭に「”FEFF”」を付けることで文字化けしません。例えば下記に「Collection」の記述例を示します。
「Collection/Schema」はPDFポートフォリオのフォームのラベル内容、「Name/Names/EmbeddedFiles/Names」はPDFポートフォリオで表示する各ファイルの情報、「Collection/Split」はPDFポートフォリオの表示領域を設定します。「Name/Names/EmbeddedFiles/Names」は通常は/CIの<< >>内を編集するだけでよいと思います。また、情報のラベルは「Collection/Schema」の内容と同じでなければいけません。今回の例ではfrom、subject、rdate、attacheNです。
- PDFポートフォリオの例
- Collectionの記述例
<< /Type /Catalog /Pages 2 0 R /Collection << /Type /Collection /Schema << /from << /Subtype /S /N <FEFF5DEE51FA4EBA> /O 1 /V true /E false >> /subject << /Subtype /S /N <FEFF4EF6540D> /O 2 /V true /E false >> /rdate << /Subtype /S /N <FEFF65E54ED8> /O 3 /V true /E false >> /attacheN << /Subtype /S /N <FEFF6DFB4ED830D530A130A430EB6570> /O 4 /V true /E false >> >> /Sort << /S /rdate /A false >> /View /D /Split << /Direction /H /Position 15 >> >> /Names << /EmbeddedFiles << /Names [ (FEFF004d006500730073006100670065003000300030002e007000640066) << /CI << /Type /CollectionItem /from <FEFF0069006E0066006F0040006B00610072006100640061002D0067006F006F0064002E006E00650074> /subject <FEFF30D630ED30B030C630B930C830E130FC30EB> /rdate (2021-05-05 12:18:30) /attacheN (1) >> /EF << /F 3 0 R >> /F <FEFF30D630ED30B030C630B930C830E130FC30EB002E007000640066> /UF <FEFF30D630ED30B030C630B930C830E130FC30EB002E007000640066> /Desc (FEFF004d006500730073006100670065003000300030002e007000640066) /Type /Filespec >> ] >> >> /PageMode /UseAttachments >>
PDFポートフォリオの作成
- 日本語フォントファイル
IPAexゴシック(Ver.004.01)を利用します。URLよりダウンロードしてPDFポートフォリオスクリプト実行ファイルと同じ場所(カレントフォルダ)に保存してください。詳しい方は同じ場所でなくともよいです。スクリプト内でフォントファイルの場所を設定してください。
IPAexフォント:https://moji.or.jp/ipafont/ipaex00401/
- pyMuPDFライブラリを使用したPDFポートフォリオスクリプト
次にPyMuPDF(importするときは’fitz’)ライブラリを利用して、先ほどのメール情報を利用してPDFポートフォリオを作成します。スクリプトはページ上部の「情報取得のスクリプト」から順番に実行してください。
スクリプトを実行するとカレントフォルダにメールの添付ファイル、メール内容を書き込んだ「Test.pdf」と「Test.pdf」を組み込んだPDFポートフォリオ「PortFolio.pdf」が保存されます。本当はtempフォルダなどを利用してメール内容のPDFを消去したり、文字数やページ当たりの行数などを調整するのがよいです。あくまでPDFポートフォリオ作成の基本的な内容です。スクリプト内のコメントを参考に改良してみてください。
まずは必要なライブラリを読み込みます。osライブラリはカレントフォルダの場所を取得、reライブラリは文字の置換に使用しています。
import fitz import os import re
メール内容を書き込んだ「Test.pdf」作成スクリプト
###選択メールPDFを「Test.pdf」作成##### #PDFファイルを作成 doc = fitz.open() page = doc.new_page() #新規ページ #フォントファイルの設定 ffile = 'ipaexg.ttf' font = 'F0' #用紙サイズ情報 width, height = fitz.PaperSize('a4') #情報の書き込み page.insert_textbox(fitz.Rect(30, 40, 100, 100), '受信日:', fontsize = 9, fontname = font, fontfile = ffile) page.insert_textbox(fitz.Rect(70, 40, 250, 150), ItemDate, fontsize = 9, fontname = font, fontfile = ffile) page.insert_textbox(fitz.Rect(30, 51, 100, 100), '件 名:', fontsize = 9, fontname = font, fontfile = ffile) page.insert_textbox(fitz.Rect(70, 51, 550, 350), ItemSubject, fontsize = 9, fontname = font, fontfile = ffile) page.insert_textbox(fitz.Rect(30, 62, 100, 100), '差出人:', fontsize = 9, fontname = font, fontfile = ffile) page.insert_textbox(fitz.Rect(70, 62, 250, 150), ItemFrom, fontsize = 9, fontname = font, fontfile = ffile) page.insert_textbox(fitz.Rect(30, 72, 100, 100), '宛 先:', fontsize = 9, fontname = font, fontfile = ffile) page.insert_textbox(fitz.Rect(70, 72, 550, 350), ItemTo, fontsize = 9, fontname = font, fontfile = ffile) page.insert_textbox(fitz.Rect(30, 82, 1000, 1000), 'CC:', fontsize = 9, fontname = font, fontfile = ffile) page.insert_textbox(fitz.Rect(70, 82, 550, 350), ItemCc, fontsize = 9, fontname = font, fontfile = ffile) page.drawLine(fitz.Point(30, 92), fitz.Point(550, 92), color = (0, 0, 0), width = 0.7, dashes = "[3] 0") #本文の書き込み FarstRect = fitz.Rect(30, 95, width, height) #書き込み位置の設定 page.insert_textbox(FarstRect, ItemMessage, fontname = font, fontfile = ffile) #本文の書き込み #添付ファイル処理 if ItemAttachementsCnt != 0: for fcnt, attachment in enumerate(ItemAttachements): fcnt = fcnt + 1 fname = str(fcnt) + '.' + str(attachment).split('.')[-1] #添付ファイルの保存 print(attachment) attachment.SaveASFile(os.getcwd() + '\\' + str(attachment)) #ファイルの埋め込み doc.embfile_add(fname, bytearray(open(str(attachment), 'rb').read()), filename = str(attachment)) #PDF内のフォントをサブセット化(ファイルサイズの減少) doc.subset_fonts() #Test.pdfの保存 doc.save('Test.pdf', garbage = 4, deflate = True)
ここまでで、下記のPDFが作成できます。
「Test.pdf」を組み込んだPDFポートフォリオ「PortFolio.pdf」作成スクリプト
#ポートフォリオの作成 doc = fitz.open() page = doc.new_page(width=50, height=50) #page = doc.new_page() #プレビュー表紙へロゴの書き込み #page.insertImage(fitz.Rect(30, -50, 130, 100), stream = open(os.getcwd() + '\\' + 'Karada_good_logo.png', 'rb').read() #プレビュー表紙へ書き込み Rect = fitz.Rect(2, 20, 1300, 1000) #書き込み位置の設定 page.insert_textbox(Rect, "KARADA GOOD.net", fontsize = 5, color = (0.25, 0.25, 0.25), fontname = font, fontfile = ffile) #ポートフォリオフォームの表示項目設定 doc.xref_set_key(1, 'Collection/Type', '/Collection') #差出人 doc.xref_set_key(1, 'Collection/Schema/from', '<</Subtype /S /N <FEFF5DEE51FA4EBA> /O 1 /V true /E false>>') #件名 doc.xref_set_key(1, 'Collection/Schema/subject', '<</Subtype /S /N <FEFF4EF6540D> /O 2 /V true /E false>>') #日付 doc.xref_set_key(1, 'Collection/Schema/rdate', '<</Subtype /S /N <FEFF65E54ED8> /O 3 /V true /E false>>') #添付ファイル数 doc.xref_set_key(1, 'Collection/Schema/attacheN', '<</Subtype /S /N <FEFF6DFB4ED830D530A130A430EB6570> /O 4 /V true /E false>>') #sort設定 doc.xref_set_key(1, 'Collection/Sort', '<</S /rdate /A false>>') #表示設定 doc.xref_set_key(1, 'Collection/View', '/D') #表示領域の設定 doc.xref_set_key(1, 'Collection/Split', '<</Direction /H /Position 15>>') #ポートフォリオに追加 for cnt, message in enumerate(messages): #ファイルの埋め込み AddFileName = 'Test.pdf' AddFileName = 'FEFF' + AddFileName.encode('utf-16BE', 'replace').hex() doc.embfile_add(AddFileName, bytearray(open(os.getcwd() + '\\' + 'Test.pdf', 'rb').read()), filename = ItemSubject + '.pdf') #PDFファイルプロパティの取得 CIPropaty = doc.xref_get_key(1, 'Names/EmbeddedFiles/Names')[1] CIPropaty = re.sub('\[|\]', '', CIPropaty) CIPropaty = CIPropaty.replace('/Type/Filespec>>', '/Type/Filespec>>,') CIPropaty = CIPropaty.split(',') del CIPropaty[-1] #差出人処理,ビッグエンディアンにする MSender = 'FEFF' + ItemFrom.encode('utf-16BE', 'replace').hex() #件名処理 MSubject = 'FEFF' + ItemSubject.encode('utf-16BE', 'replace').hex() #受信日処理 MRtime = ItemDate #添付ファイル数 MAtFiles = str(ItemAttachementsCnt) #PDFファイルプロパティの書き込み MakeCI = '<</Type /CollectionItem /from <' + MSender + '> /subject <' + MSubject + '> /rdate (' + MRtime + ') /attacheN (' + MAtFiles + ')>>' for index, item in enumerate(CIPropaty): CIPropaty[index] = item.replace('/CI<<>>', '/CI' + MakeCI) MCIPropaty = ''.join(CIPropaty) MCIPropaty = '[' + MCIPropaty + ']' doc.xref_set_key(1, 'Names/EmbeddedFiles/Names', MCIPropaty) #PDFプロパティの書き込み m = {'creationDate': fitz.getPDFnow(), 'modDate': fitz.getPDFnow(), 'creator': 'KARADAGOOD.net', 'producer': 'PyMuPDF %s' % fitz.VersionBind, 'title': 'KARADAGOOD.net', 'subject': 'KARADAGOOD.net', 'author': 'KARADAGOOD.net'} doc.setMetadata(m) #ドキュメントの保存 #PDF内のフォントをサブセット化(ファイルサイズの減少) doc.subset_fonts() doc.save('PortFolio.pdf', garbage = 4, deflate = True) doc.close()
以上で下記のPDFが作成できます。
- エクスプローラーのプレビューウィンドウでの表示
- Acrobat Readerでの表示
参考までに作成したPDFファイルです。Acrobatで開くことをお勧めします。
参考資料
・Document management -Portable document format- Part1: PDF1.7: https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf
・Adobe Supplement to the ISO 3200: https://www.adobe.com/content/dam/acom/en/devnet/pdf/adobe_supplement_iso32000.pdf
・PyMuPDF Documentation: https://pymupdf.readthedocs.io/en/latest/index.html
少しでも、誰かの役に立ちますように!!