使用Python開發(fā)一個桌面版PDF蓋章工具
一、項目概述
這個工具的主要功能包括:
- 打開和預(yù)覽PDF文件
- 加載印章圖片(支持PNG、JPG等格式)
- 在PDF頁面上精確點擊添加印章
- 調(diào)整印章大小
- 撤銷操作、清空頁面印章
- 保存蓋章后的PDF文件
二、環(huán)境準(zhǔn)備
首先需要安裝必要的Python庫:
pip install PyQt5 PyMuPDF pillow
注意:PyMuPDF是fitz模塊的庫,通過pip install PyMuPDF安裝。
三、核心代碼解析
1. PDF預(yù)覽編輯畫布類
class PdfEditPreview(QLabel):
# 信號:點擊坐標(biāo) (x, y)
clicked_pos = pyqtSignal(float, float)
def __init__(self):
super().__init__()
self.setAlignment(Qt.AlignCenter)
self.setStyleSheet('background: #e0e0e0; color: #888;')
self.setText("請打開 PDF 文件")
self.doc = None # PDF文檔對象
self.page_index = 0 # 當(dāng)前頁碼
self.current_pixmap = None # 當(dāng)前頁面圖像
self.stamps_to_draw = [] # 當(dāng)前頁印章列表這個類繼承自QLabel,負責(zé)顯示PDF頁面并處理用戶點擊。關(guān)鍵點:
- 坐標(biāo)轉(zhuǎn)換:將屏幕點擊坐標(biāo)轉(zhuǎn)換為PDF點坐標(biāo)(1點=1/72英寸)
- 自適應(yīng)高度:根據(jù)PDF頁面比例自動調(diào)整顯示高度
- 印章疊加:在PDF頁面上繪制已添加的印章
2. 印章添加邏輯
def add_stamp(self, pdf_x, pdf_y):
if not self.doc or self.seal_bytes is None:
QMessageBox.warning(self, "提示", "請先加載 PDF 和 印章圖片!")
return
scale = self.spin_scale.value() / 100.0
# 計算印章在PDF中的實際尺寸
actual_w = self.seal_w_pt * scale
actual_h = actual_w * self.seal_ratio
stamp_record = {
'page': self.current_page,
'x': pdf_x, # PDF坐標(biāo)X
'y': pdf_y, # PDF坐標(biāo)Y
'w_pt': actual_w, # 印章寬度(點)
'h_pt': actual_h, # 印章高度(點)
'img_bytes': self.seal_bytes, # 印章圖片原始數(shù)據(jù)
'pixmap': self.seal_pixmap # 用于顯示的QPixmap
}
self.stamps_list.append(stamp_record)
self.refresh_preview()3. 滾動預(yù)覽的修復(fù)
原版本存在預(yù)覽區(qū)域底部被裁剪的問題,修復(fù)方案:
# 創(chuàng)建滾動區(qū)域
self.scroll_area = QScrollArea()
self.scroll_area.setWidget(self.preview)
self.scroll_area.setWidgetResizable(True) # 關(guān)鍵:讓預(yù)覽組件寬度跟隨滾動區(qū)
self.scroll_area.setStyleSheet("border: 2px dashed #aaa;")
# 在預(yù)覽類中實現(xiàn)自適應(yīng)高度
def adjust_height(self):
"""根據(jù)當(dāng)前控件寬度和圖片比例,自動調(diào)整控件高度"""
if not self.current_pixmap or self.img_w_raw == 0:
return
# 計算寬高比
ratio = self.img_h_raw / self.img_w_raw
# 目標(biāo)高度 = 當(dāng)前寬度 * 比例
target_height = int(self.width() * ratio)
# 設(shè)置最小高度,這樣ScrollArea就會出現(xiàn)滾動條
if self.minimumHeight() != target_height:
self.setMinimumHeight(target_height)4. 保存PDF文件
def save_pdf(self):
if not self.doc: return
if not self.stamps_list:
QMessageBox.information(self, "提示", "當(dāng)前沒有蓋任何章,不需要保存。")
return
save_path, _ = QFileDialog.getSaveFileName(
self, "保存文件",
self.pdf_path.replace(".pdf", "_stamped.pdf"),
"PDF Files (*.pdf)"
)
for stamp in self.stamps_list:
page = self.doc[stamp['page']]
# 計算印章在PDF中的位置
rect_x0 = stamp['x'] - stamp['w_pt'] / 2
rect_y0 = stamp['y'] - stamp['h_pt'] / 2
rect_x1 = stamp['x'] + stamp['w_pt'] / 2
rect_y1 = stamp['y'] + stamp['h_pt'] / 2
rect = fitz.Rect(rect_x0, rect_y0, rect_x1, rect_y1)
# 將印章插入PDF
page.insert_image(rect, stream=stamp['img_bytes'])四、界面布局設(shè)計
工具界面分為三個主要區(qū)域:
+----------------+-------------------------------+ | 左側(cè)工具欄 | PDF預(yù)覽區(qū) | | | (帶滾動條,支持長頁面) | +----------------+-------------------------------+ | | 頁面導(dǎo)航條 | +----------------+-------------------------------+
左側(cè)工具欄包含:
- 文件操作區(qū)域:打開PDF文件
- 印章設(shè)置區(qū)域:選擇印章圖片、調(diào)整大小
- 編輯控制區(qū)域:撤銷、清空操作
- 保存按鈕
五、使用步驟
- 打開PDF文件:點擊"打開PDF"按鈕選擇文件
- 加載印章:點擊"選擇印章"按鈕,支持PNG、JPG等格式
- 調(diào)整印章大小:通過縮放比例旋鈕調(diào)整(5%-500%)
- 添加印章:在PDF預(yù)覽區(qū)域點擊鼠標(biāo)左鍵
- 編輯操作:可撤銷最后一個印章或清空當(dāng)前頁
- 保存文件:點擊"另存為PDF"保存蓋章后的文件
六、技術(shù)要點
- 坐標(biāo)系統(tǒng)轉(zhuǎn)換:需要在屏幕像素坐標(biāo)、PDF點坐標(biāo)和圖像顯示坐標(biāo)之間進行精確轉(zhuǎn)換
- 圖像處理:使用Pillow處理印章圖片的透明通道
- 內(nèi)存管理:及時關(guān)閉PDF文檔,避免內(nèi)存泄漏
- 用戶體驗:添加撤銷功能、實時預(yù)覽和錯誤提示
七、完整代碼
以下是完整的Python代碼:
import sys
import os
import fitz # PyMuPDF
from io import BytesIO
from PIL import Image
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QGridLayout, QPushButton, QLabel, QFileDialog, QMessageBox,
QSpinBox, QGroupBox, QLineEdit, QSplitter, QScrollArea
)
from PyQt5.QtCore import Qt, pyqtSignal, QRect
from PyQt5.QtGui import QPixmap, QImage, QPainter, QPen, QColor, QIcon
# ===========================
# PDF 預(yù)覽/編輯畫布
# ===========================
class PdfEditPreview(QLabel):
# 信號:點擊坐標(biāo) (x, y)
clicked_pos = pyqtSignal(float, float)
def __init__(self):
super().__init__()
self.setAlignment(Qt.AlignCenter)
# 去掉固定的 border 樣式,改由 ScrollArea 管理外觀,這里只保留背景
self.setStyleSheet('background: #e0e0e0; color: #888;')
self.setText("請打開 PDF 文件")
self.doc = None
self.page_index = 0
self.current_pixmap = None
# 存儲當(dāng)前頁面的已蓋章列表 (由主窗口傳入)
self.stamps_to_draw = []
# 坐標(biāo)轉(zhuǎn)換參數(shù)
self.img_w_raw = 0.0
self.img_h_raw = 0.0
def load_page(self, doc, index):
self.doc = doc
self.page_index = index
self.render()
def render(self):
if not self.doc: return
page = self.doc[self.page_index]
# 獲取 120 DPI 的圖像用于顯示
pix = page.get_pixmap(dpi=120)
self.img_w_raw = pix.width
self.img_h_raw = pix.height
img = QImage(pix.samples, self.img_w_raw, self.img_h_raw, pix.stride, QImage.Format_RGB888)
self.current_pixmap = QPixmap.fromImage(img)
# 加載新圖片后,立即調(diào)整控件高度以適應(yīng)比例
self.adjust_height()
self.update()
def adjust_height(self):
"""根據(jù)當(dāng)前控件寬度和圖片比例,自動調(diào)整控件高度,確保底部不被裁剪"""
if not self.current_pixmap or self.img_w_raw == 0:
return
# 計算寬高比
ratio = self.img_h_raw / self.img_w_raw
# 目標(biāo)高度 = 當(dāng)前寬度 * 比例
target_height = int(self.width() * ratio)
# 設(shè)置最小高度,這樣 ScrollArea 就會出現(xiàn)滾動條
if self.minimumHeight() != target_height:
self.setMinimumHeight(target_height)
def resizeEvent(self, event):
"""當(dāng)窗口大小改變(寬度改變)時,重新計算高度"""
super().resizeEvent(event)
self.adjust_height()
def set_stamps(self, stamps_list):
"""接收主窗口傳來的當(dāng)前頁蓋章數(shù)據(jù),用于重繪"""
self.stamps_to_draw = stamps_list
self.update()
def mousePressEvent(self, e):
if not self.doc or not self.current_pixmap: return
# 計算圖片在 Label 中的顯示區(qū)域(居中)
scaled_pix = self.current_pixmap.scaledToWidth(self.width(), Qt.SmoothTransformation)
img_x_start = (self.width() - scaled_pix.width()) / 2
img_y_start = (self.height() - scaled_pix.height()) / 2
# 獲取相對于圖片的點擊坐標(biāo)
click_x = e.x() - img_x_start
click_y = e.y() - img_y_start
# 檢查是否點擊在圖片范圍內(nèi)
if click_x < 0 or click_x > scaled_pix.width() or \
click_y < 0 or click_y > scaled_pix.height():
return
# 轉(zhuǎn)換為 PDF 點坐標(biāo) (PDF Points)
pdf_page = self.doc[self.page_index]
scale = pdf_page.rect.width / scaled_pix.width()
pdf_x = click_x * scale
pdf_y = click_y * scale
self.clicked_pos.emit(pdf_x, pdf_y)
def paintEvent(self, e):
super().paintEvent(e) # 繪制背景
if not self.doc or not self.current_pixmap: return
painter = QPainter(self)
painter.setRenderHint(QPainter.SmoothPixmapTransform)
# 1. 繪制 PDF 底圖
# 使用 scaledToWidth 確保寬度填滿,高度隨 resizeEvent 自動調(diào)整
scaled_pix = self.current_pixmap.scaledToWidth(self.width(), Qt.SmoothTransformation)
img_x_start = (self.width() - scaled_pix.width()) / 2
img_y_start = (self.height() - scaled_pix.height()) / 2
painter.drawPixmap(int(img_x_start), int(img_y_start), scaled_pix)
# 2. 繪制已確認的印章 (Overlay)
pdf_page = self.doc[self.page_index]
pt_to_px = scaled_pix.width() / pdf_page.rect.width
for stamp in self.stamps_to_draw:
# 尺寸轉(zhuǎn)屏幕像素
w_screen = stamp['w_pt'] * pt_to_px
h_screen = stamp['h_pt'] * pt_to_px
# 中心坐標(biāo)轉(zhuǎn)屏幕像素
cx_screen = stamp['x'] * pt_to_px
cy_screen = stamp['y'] * pt_to_px
# 計算繪制左上角
draw_x = img_x_start + cx_screen - (w_screen / 2)
draw_y = img_y_start + cy_screen - (h_screen / 2)
target_rect = QRect(int(draw_x), int(draw_y), int(w_screen), int(h_screen))
painter.drawPixmap(target_rect, stamp['pixmap'])
# 繪制一個小紅框表示這是后加的章
painter.setPen(QPen(QColor(255, 0, 0, 100), 1, Qt.DashLine))
painter.drawRect(target_rect)
painter.end()
# ===========================
# 主窗口
# ===========================
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# --- 數(shù)據(jù)狀態(tài) ---
self.doc = None
self.pdf_path = ""
self.current_page = 0
self.stamps_list = []
self.seal_pixmap = None
self.seal_bytes = None
self.seal_w_pt = 0.0
self.seal_ratio = 1.0
self.init_ui()
def init_ui(self):
self.setWindowTitle('PDF 手動蓋章編輯器 (滾動預(yù)覽修復(fù)版)')
self.setGeometry(100, 100, 1300, 850)
main_widget = QWidget()
self.setCentralWidget(main_widget)
main_layout = QHBoxLayout(main_widget)
# ===== 左側(cè):工具欄 (保持不變) =====
tools_panel = QVBoxLayout()
tools_panel.setContentsMargins(0, 0, 10, 0)
gb_file = QGroupBox("1. 文件操作")
v_file = QVBoxLayout(gb_file)
btn_open = QPushButton("?? 打開 PDF")
btn_open.setStyleSheet("padding: 8px; font-weight: bold;")
btn_open.clicked.connect(self.open_pdf)
self.lbl_info = QLabel("未加載文件")
self.lbl_info.setStyleSheet("color: #666; font-size: 11px;")
v_file.addWidget(btn_open)
v_file.addWidget(self.lbl_info)
gb_seal = QGroupBox("2. 印章設(shè)置")
v_seal = QVBoxLayout(gb_seal)
self.txt_seal_path = QLineEdit()
self.txt_seal_path.setPlaceholderText("選擇印章圖片...")
self.txt_seal_path.setReadOnly(True)
btn_sel_seal = QPushButton("??? 選擇印章")
btn_sel_seal.clicked.connect(self.select_seal)
self.spin_scale = QSpinBox()
self.spin_scale.setRange(5, 500)
self.spin_scale.setValue(35)
self.spin_scale.setSuffix(" %")
self.spin_scale.valueChanged.connect(self.update_seal_preview_info)
self.lbl_seal_preview = QLabel("印章預(yù)覽")
self.lbl_seal_preview.setAlignment(Qt.AlignCenter)
self.lbl_seal_preview.setFixedSize(150, 150)
self.lbl_seal_preview.setStyleSheet("border: 1px solid #ddd; background: #fff;")
v_seal.addWidget(btn_sel_seal)
v_seal.addWidget(self.txt_seal_path)
v_seal.addWidget(QLabel("縮放比例:"))
v_seal.addWidget(self.spin_scale)
v_seal.addWidget(self.lbl_seal_preview, 0, Qt.AlignCenter)
gb_action = QGroupBox("3. 編輯控制")
v_action = QVBoxLayout(gb_action)
btn_undo = QPushButton("?? 撤銷上一個印章")
btn_undo.clicked.connect(self.undo_last_stamp)
btn_clear_page = QPushButton("??? 清空當(dāng)前頁印章")
btn_clear_page.clicked.connect(self.clear_page_stamps)
v_action.addWidget(btn_undo)
v_action.addWidget(btn_clear_page)
btn_save = QPushButton("?? 另存為 PDF")
btn_save.setFixedHeight(50)
btn_save.setStyleSheet("background-color: #28a745; color: white; font-size: 14px; font-weight: bold;")
btn_save.clicked.connect(self.save_pdf)
tools_panel.addWidget(gb_file)
tools_panel.addWidget(gb_seal)
tools_panel.addWidget(gb_action)
tools_panel.addStretch()
tools_panel.addWidget(btn_save)
# ===== 中間:預(yù)覽區(qū) (修復(fù)重點) =====
preview_layout = QVBoxLayout()
self.preview = PdfEditPreview()
self.preview.clicked_pos.connect(self.add_stamp)
# --- 創(chuàng)建滾動區(qū)域 ---
self.scroll_area = QScrollArea()
self.scroll_area.setWidget(self.preview)
self.scroll_area.setWidgetResizable(True) # 關(guān)鍵:讓預(yù)覽組件寬度跟隨滾動區(qū),但高度由組件自己決定
self.scroll_area.setStyleSheet("border: 2px dashed #aaa;")
# 翻頁條
nav_layout = QHBoxLayout()
self.btn_prev = QPushButton("? 上一頁")
self.btn_next = QPushButton("下一頁 ?")
self.lbl_page = QLabel("0 / 0")
self.btn_prev.clicked.connect(self.prev_page)
self.btn_next.clicked.connect(self.next_page)
nav_layout.addWidget(self.btn_prev)
nav_layout.addWidget(self.lbl_page)
nav_layout.addWidget(self.btn_next)
preview_layout.addWidget(QLabel("<b>??? 操作說明:</b>加載 PDF 和印章后,直接在右側(cè)頁面上<b>點擊鼠標(biāo)左鍵</b>即可蓋章。"))
preview_layout.addWidget(self.scroll_area, 1) # 添加滾動區(qū)域而不是直接添加 preview
preview_layout.addLayout(nav_layout)
# 組合布局
tools_widget = QWidget()
tools_widget.setLayout(tools_panel)
tools_widget.setFixedWidth(280)
main_layout.addWidget(tools_widget)
main_layout.addLayout(preview_layout)
self.update_ui_state()
# ---------- 邏輯功能 (保持不變) ----------
def open_pdf(self):
path, _ = QFileDialog.getOpenFileName(self, "打開 PDF", "", "PDF Files (*.pdf)")
if not path: return
try:
if self.doc: self.doc.close()
self.doc = fitz.open(path)
self.pdf_path = path
self.current_page = 0
self.stamps_list = []
self.lbl_info.setText(os.path.basename(path))
self.refresh_preview()
self.update_ui_state()
except Exception as e:
QMessageBox.critical(self, "錯誤", f"無法打開文件:\n{str(e)}")
def select_seal(self):
path, _ = QFileDialog.getOpenFileName(self, "選擇印章圖片", "", "Images (*.png *.jpg *.jpeg *.bmp)")
if not path: return
self.txt_seal_path.setText(path)
try:
pil_img = Image.open(path)
if pil_img.mode != 'RGBA':
pil_img = pil_img.convert('RGBA')
byte_io = BytesIO()
pil_img.save(byte_io, format='PNG')
self.seal_bytes = byte_io.getvalue()
img_doc = fitz.open("png", self.seal_bytes)
page = img_doc.load_page(0)
self.seal_w_pt = page.rect.width
self.seal_ratio = page.rect.height / page.rect.width
img_doc.close()
self.seal_pixmap = QPixmap(path)
self.lbl_seal_preview.setPixmap(self.seal_pixmap.scaled(
self.lbl_seal_preview.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
))
self.update_seal_preview_info()
except Exception as e:
QMessageBox.critical(self, "圖片錯誤", f"處理印章圖片失敗:\n{e}")
def update_seal_preview_info(self):
pass
def add_stamp(self, pdf_x, pdf_y):
if not self.doc or self.seal_bytes is None:
QMessageBox.warning(self, "提示", "請先加載 PDF 和 印章圖片!")
return
scale = self.spin_scale.value() / 100.0
actual_w = self.seal_w_pt * scale
actual_h = actual_w * self.seal_ratio
stamp_record = {
'page': self.current_page,
'x': pdf_x,
'y': pdf_y,
'w_pt': actual_w,
'h_pt': actual_h,
'img_bytes': self.seal_bytes,
'pixmap': self.seal_pixmap
}
self.stamps_list.append(stamp_record)
self.refresh_preview()
def undo_last_stamp(self):
if not self.stamps_list: return
removed = self.stamps_list.pop()
if removed['page'] == self.current_page:
self.refresh_preview()
else:
QMessageBox.information(self, "撤銷", f"已撤銷第 {removed['page']+1} 頁上的印章")
def clear_page_stamps(self):
old_len = len(self.stamps_list)
self.stamps_list = [s for s in self.stamps_list if s['page'] != self.current_page]
if len(self.stamps_list) < old_len:
self.refresh_preview()
def refresh_preview(self):
if not self.doc: return
current_page_stamps = [s for s in self.stamps_list if s['page'] == self.current_page]
self.preview.set_stamps(current_page_stamps)
self.preview.load_page(self.doc, self.current_page)
self.lbl_page.setText(f"{self.current_page + 1} / {len(self.doc)}")
self.update_ui_state()
def prev_page(self):
if self.current_page > 0:
self.current_page -= 1
self.refresh_preview()
# 翻頁時重置滾動條到頂部
self.scroll_area.verticalScrollBar().setValue(0)
def next_page(self):
if self.doc and self.current_page < len(self.doc) - 1:
self.current_page += 1
self.refresh_preview()
# 翻頁時重置滾動條到頂部
self.scroll_area.verticalScrollBar().setValue(0)
def update_ui_state(self):
has_doc = self.doc is not None
self.btn_prev.setEnabled(has_doc and self.current_page > 0)
self.btn_next.setEnabled(has_doc and self.current_page < len(self.doc) - 1)
def save_pdf(self):
if not self.doc: return
if not self.stamps_list:
QMessageBox.information(self, "提示", "當(dāng)前沒有蓋任何章,不需要保存。")
return
save_path, _ = QFileDialog.getSaveFileName(self, "保存文件", self.pdf_path.replace(".pdf", "_stamped.pdf"), "PDF Files (*.pdf)")
if not save_path: return
try:
for stamp in self.stamps_list:
page = self.doc[stamp['page']]
rect_x0 = stamp['x'] - stamp['w_pt'] / 2
rect_y0 = stamp['y'] - stamp['h_pt'] / 2
rect_x1 = stamp['x'] + stamp['w_pt'] / 2
rect_y1 = stamp['y'] + stamp['h_pt'] / 2
rect = fitz.Rect(rect_x0, rect_y0, rect_x1, rect_y1)
page.insert_image(rect, stream=stamp['img_bytes'])
self.doc.save(save_path)
QMessageBox.information(self, "成功", f"文件已保存至:\n{save_path}")
self.doc.close()
self.doc = fitz.open(save_path)
self.pdf_path = save_path
self.stamps_list = []
self.refresh_preview()
except Exception as e:
QMessageBox.critical(self, "保存失敗", str(e))
if __name__ == '__main__':
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())八、擴展功能建議
- 批量蓋章:支持在多個位置批量添加相同印章
- 印章庫管理:保存常用的多個印章,方便快速選擇
- 模板功能:保存常用的蓋章位置模板
- 文字水印:除了圖片印章,支持添加文字水印
- 多頁操作:支持跨頁復(fù)制印章位置
九、總結(jié)
通過這個項目,我們實現(xiàn)了:
- 一個完整的桌面GUI應(yīng)用
- PDF文件的解析和渲染
- 精確的坐標(biāo)定位系統(tǒng)
- 圖像與PDF的合成功能
- 友好的用戶交互界面
這個工具不僅實用,也是學(xué)習(xí)PyQt5圖形界面編程和PDF處理的好例子。
到此這篇關(guān)于使用Python開發(fā)一個桌面版PDF蓋章工具的文章就介紹到這了,更多相關(guān)Python PDF蓋章工具內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在 Django/Flask 開發(fā)服務(wù)器上使用 HTTPS
使用 Django 或 Flask 這種框架開發(fā) web app 的時候一般都會用內(nèi)建服務(wù)器開發(fā)和調(diào)試程序,等程序完成后再移交到生產(chǎn)環(huán)境部署。問題是這些內(nèi)建服務(wù)器通常都不支持 HTTPS,那么我們來探討下開啟https吧2014-07-07
python+unittest+requests實現(xiàn)接口自動化的方法
這篇文章主要介紹了python+unittest+requests實現(xiàn)接口自動化的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11
使用Python實現(xiàn)實時金價監(jiān)控并自動提醒功能
在日常投資中,很多朋友喜歡在一些平臺買點黃金,低買高賣賺點小差價,但黃金價格實時波動頻繁,總是盯著手機太累了,于是我用Python寫了一個實時金價監(jiān)控+自動提醒腳本,可以幫我在金價波動達到盈虧閾值時自動彈窗提醒,告別手動盯盤,需要的朋友可以參考下2025-05-05
Python3字符串的常用操作方法之修改方法與大小寫字母轉(zhuǎn)化
這篇文章主要介紹了Python3字符串的常用操作方法之修改方法與大小寫字母轉(zhuǎn)化,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09

