Python导出《你的日记》为PDF

项目是由Cierra-Runis/nideriji-exporter派生而来,在此基础上进行改造和扩充

地址:zzlKevin/nideriji-exporter-PDF: 新增导出纯文字pdf 以及 包含图片的PDF (github.com)

新增功能:

1、生成纯文字PDF 和 带图片的PDF

2、优化心情、天气的描述

输出纯文字PDF 与 带图片的PDF 预览:

Imgpdf.py:

引入的库和模块,使用from.....import..... 的形式按需导入,可以使后续转exe大幅减少文件大小

import json
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image as RLImage
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from PIL import Image
import os
import tkinter as tk
from tkinter import filedialog

表情和天气字典

MOOD_DICT: dict = {
    'excited': '大笑',
    'tongue': '滑稽',
    'cool': '很酷',
    'devil': '生气的魔鬼',
    'happy': '开心',
    'poop': '一坨粑粑',
    'neutral': '平静',
    'sad': '失落',
    'dead': '原地去世',
    'normal': '正常',
}

WEATHER_DICT: dict = {
    'lightning-rainy': '闪电-下雨',
    'pouring': '暴雨',
    'snow': '下雪',
    'snowy': '大雪',
    'cloudy': '多云',
    'sunny': '晴朗',
    'rainy': '下雨',
    'fog': '雾霾',
    'windy': '大风',
    'hail': '冰雹',
}

 注册中文字体,添加字体样式

# 注册中文字体
pdfmetrics.registerFont(TTFont('SimHei', 'simHei.ttf'))  # 确保你有这个字体文件
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name='diaryTitle', fontName='SimHei', fontSize=18, alignment=1, bold=True))
styles.add(ParagraphStyle(name='Chinese', fontName='SimHei', fontSize=12))
styles.add(ParagraphStyle(name='RightAlign', fontName='SimHei', fontSize=12, alignment=2))
styles.add(ParagraphStyle(name='Center', fontName='SimHei', fontSize=12, alignment=1))  #居中的图片数字样式

读取日记json文件 

# 读取日记条目的JSON数据
def read_diaries_json(file_path):
   with open(file_path, 'r', encoding='utf-8') as file:
       return json.load(file)

 创建PDF的函数中:

将日记标题为空的添加“无题”

if entry['title']:
    flowables.append(title)
else:
    flowables.append(Paragraph(f'无题', styles['diaryTitle']))

 对心情、天气替换为字典中的中文

      if MOOD_DICT.get(entry["mood"]):
          flowables.append(mood)
      if WEATHER_DICT.get(entry["weather"]):
          flowables.append(weather)

 处理图片的部分:

先将图片的大小缩小到原来的五分之一,同时使用Lanczos算法来保持图片质量

image = Image.open(img_path)
image = image.resize((image.width // 5, image.height // 5), Image.Resampling.LANCZOS)

然后根据PDF的大小动态调整剩余仍然过大的图片的大小 ,并添加图片序号

# 获取PDF框架的大小
frame_width = pdf.width - pdf.leftMargin - pdf.rightMargin
frame_height = pdf.height - pdf.topMargin - pdf.bottomMargin

# 计算图像缩放因子,保持宽高比
scale = min(frame_width / image.width, frame_height / image.height)
if scale > 1:  # 如果缩放因子大于1,则不需要放大,直接使用原始尺寸
      scale = 1

# 调整图像大小
new_width = image.width * scale
new_height = image.height * scale

rl_image = RLImage(img_path, width=new_width, height=new_height)
# 图片可能需要居中,可以使用Spacer来调整位置
# flowables.append(Spacer(1, (frame_height - new_height) / 2))  # 临时Spacer,确保图片垂直居中
flowables.append(Spacer(1, 12))  # 添加一些空间
flowables.append(rl_image)
flowables.append(Paragraph(f'[图{img_key}]', styles['Center']))
flowables.append(Spacer(1, 12))  # 添加一些空间

 完整的创建PDF的代码:

# 创建PDF文件
def create_pdf(diaries, images_folder, file_name):
   # 创建PDF文档
   pdf = SimpleDocTemplate(file_name, pagesize=letter,
                           rightMargin=72, leftMargin=72,
                           topMargin=72, bottomMargin=72)
   
   # 为每篇日记创建段落,并插入图片
   flowables = []
   for entry in diaries:
      title = Paragraph(entry['title'], styles['diaryTitle'])
      mood = Paragraph(f'心情: {MOOD_DICT.get(entry["mood"], entry["mood"])}', styles['Chinese'])
      weather = Paragraph(f'天气: {WEATHER_DICT.get(entry["weather"], entry["weather"])}', styles['Chinese'])
      createddate = Paragraph(f'创建日期: {entry["createddate"]}', styles['Chinese'])
      # deleteddate = Paragraph(f'删除日期: {entry["deleteddate"]}', styles['Chinese'])
      modified_time = Paragraph(f'最后修改时间: {entry["ts"]}', styles['RightAlign'])
      created_time = Paragraph(f'创建时间: {entry["createdtime"]}', styles['Chinese'])
      
      if entry['title']:
         flowables.append(title)
      else:
         flowables.append(Paragraph(f'无题', styles['diaryTitle']))
      flowables.append(Spacer(1, 24))  # 添加更多的空间分隔日记
      flowables.append(createddate)
      # flowables.append(deleteddate)
      flowables.append(created_time)
      if MOOD_DICT.get(entry["mood"]):
          flowables.append(mood)
      if WEATHER_DICT.get(entry["weather"]):
          flowables.append(weather)
      flowables.append(Spacer(1, 12))  # 添加一些空间

      # 解析content中的图片标记,并插入图片
      content_paragraphs = entry['content'].split('\n')
      for paragraph in content_paragraphs:
            if paragraph.startswith('[图'):
               # 提取图片标记,例如'[图525]'中的'525'
               img_key = paragraph[2:-1]  # 格式为'[图标记]'
               print("正在加载图"+img_key)
               # 构建图片文件路径
               img_filename = f"图{img_key}.jpg"
               img_path = os.path.join(images_folder, img_filename)
               if os.path.exists(img_path):
                  image = Image.open(img_path)
                  image = image.resize((image.width // 5, image.height // 5), Image.Resampling.LANCZOS)
                  # 获取PDF框架的大小
                  frame_width = pdf.width - pdf.leftMargin - pdf.rightMargin
                  frame_height = pdf.height - pdf.topMargin - pdf.bottomMargin
                  
                  # 计算图像缩放因子,保持宽高比
                  scale = min(frame_width / image.width, frame_height / image.height)
                  if scale > 1:  # 如果缩放因子大于1,则不需要放大,直接使用原始尺寸
                        scale = 1
                  
                  # 调整图像大小
                  new_width = image.width * scale
                  new_height = image.height * scale
                  
                  rl_image = RLImage(img_path, width=new_width, height=new_height)
                  # 图片可能需要居中,可以使用Spacer来调整位置
                  # flowables.append(Spacer(1, (frame_height - new_height) / 2))  # 临时Spacer,确保图片垂直居中
                  flowables.append(Spacer(1, 12))  # 添加一些空间
                  flowables.append(rl_image)
                  flowables.append(Paragraph(f'[图{img_key}]', styles['Center']))
                  flowables.append(Spacer(1, 12))  # 添加一些空间

            else:
               flowables.append(Paragraph(paragraph, styles['Chinese']))
   
      flowables.append(Spacer(1, 12))  # 添加一些空间
      flowables.append(modified_time)
      flowables.append(Spacer(1, 24))  # 添加更多的空间分隔日记
      flowables.append(Spacer(1, 24))  # 添加更多的空间分隔日记
      
   print("准备输出pdf")
   # 构建PDF文档
   pdf.build(flowables)
   print("输出完毕")

三个方法分别选择日记json 文件、图片文件夹、PDF输出路径命名 :

def choose_json_file():
  root = tk.Tk()
  root.withdraw()  # 隐藏主窗口
 
  # 使用 filedialog.askopenfilename 来让用户选择文件
  json_file_path = filedialog.askopenfilename(title="选择日记JSON文件", filetypes=[("JSON files", "*.json")])
  return json_file_path

def choose_images_folder():
   root = tk.Tk()
   root.withdraw()  # 隐藏主窗口
 
   # 使用 filedialog.askdirectory 来让用户选择文件夹
   images_folder = filedialog.askdirectory(title="选择图片文件夹")
   return images_folder

def choose_output_pdf():
  root = tk.Tk()
  root.withdraw()  # 隐藏主窗口
 
  # 使用 filedialog.asksaveasfilename 来让用户选择保存文件的位置和名称
  # 设置文件类型为 PDF 文件
  output_pdf_path = filedialog.asksaveasfilename(title="选择生成PDF的路径和文件名", filetypes=[("PDF files", "*.pdf")])
  return output_pdf_path

剩余的部分:

由于原日记json文件是按时间从新到旧的降序排序,因此将其重新按从旧到新的升序排序排一遍

# 主函数
def main():
  # 让用户选择JSON文件
  print("选择日记JSON文件")
  json_file_path = choose_json_file()
  if not json_file_path:
      print("用户没有选择文件,程序退出。")
      return
 
  # 读取日记条目
  diaries = read_diaries_json(json_file_path)
 
  # 让用户选择图片文件夹
  print("选择图片文件夹")
  images_folder = choose_images_folder()
  if not images_folder:
      print("用户没有选择文件夹,程序退出。")
      return
 
  # 让用户选择生成PDF的路径和文件名
  output_pdf_path = choose_output_pdf()
  print("选择生成PDF的路径和文件名")
  if not output_pdf_path:
      print("用户没有选择输出文件,程序退出。")
      return

  # 按日记日期升序排序
  sorted_diaries = sorted(diaries, key=lambda x: x['createddate'], reverse=False)
   
  # 生成PDF
  create_pdf(sorted_diaries, images_folder, output_pdf_path)
 
if __name__ == '__main__':
   main()

纯文字的pdf.py不再赘述,只是少了图片和选择图片文件夹

使用 pyinstaller --onefile imgpdf.py 的方法将python脚本转为exe,生成的exe文件在根目录的dist文件夹中

需要 pip install pyinstaller

复杂版导出下载地址

Windows可执行文件(.exe):
https://smilelight.lanzouw.com/b030ob96hc
密码:fesr

_____________________________________________________________________________

下面是保姆式的版本,此版本在分支master2中
地址:zzlKevin/nideriji-exporter-PDF at master2 (github.com)

修改内容:

1、 nideriji_exporter.py 将其中的文件、文件夹命名都改为diary1和diary2,自己的日记为diary1,对方的日记为diary2,方便生成pdf的程序无需选择,一次生成两份自己和对方的pdf

2、将Imgpdf.py 和 pdf.py 的文件、文件夹选择部分删除,替换为固定的diary1和diary2的路径,

有些人没有匹配,所以只有自己的,所以需要加try

主函数:

def main():
 
  try:
    # 读取日记条目
    diaries1 = read_diaries_json('./.exported/json/diary1.json')
    # 按日记日期升序排序
    sorted_diaries1 = sorted(diaries1, key=lambda x: x['createddate'], reverse=False)
    images_folder1 = './.exported/img/diary1'
    output_pdf_path1 = './Imgdiary1.pdf'
    # 生成PDF
    create_pdf(sorted_diaries1, images_folder1, output_pdf_path1)
    print('生成自己的日记pdf完毕,尝试生成对方的日记pdf')
  except:
    print("没有呀?再试试对方的")
     
  try:
    diaries2 = read_diaries_json('./.exported/json/diary2.json')
    sorted_diaries2 = sorted(diaries2, key=lambda x: x['createddate'], reverse=False)
    images_folder2 = './.exported/img/diary2'
    output_pdf_path2 = './Imgdiary2.pdf'
    create_pdf(sorted_diaries2, images_folder2, output_pdf_path2)
    print('生成对方的日记pdf完毕')
  except:
    print("没有欸,可能你没有匹配")

  print("结束")

使用 pyinstaller --onefile imgpdf.py 的方法将python脚本转为exe,生成的exe文件在根目录的dist文件夹中

需要 pip install pyinstaller

保姆式导出下载地址

Windows可执行文件(.exe):

https://smilelight.lanzouw.com/b030obn9da
密码:ez47

根据exe名字的步骤依次执行
必须执行完 第一步:导出日记和图片.exe
才能执行 第二步:生成带图片的日记PDF.exe 和 第三步:生成纯文字PDF日记.exe

执行顺序:

第一步:导出日记和图片.exe

(图片多的话会有点慢,必须等待到窗口运行完毕关闭为止)

第二步:生成带图片的日记PDF.exe

(图片多的话会有点慢,耐心等待)

第三步:生成纯文字PDF日记.exe

  • 23
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zzl_Kevin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值