lightpdf-aipdf-mcp 0.1.86__py3-none-any.whl → 0.1.87__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lightpdf_aipdf_mcp/converter.py +7 -0
- lightpdf_aipdf_mcp/editor.py +62 -1
- lightpdf_aipdf_mcp/server.py +90 -13
- {lightpdf_aipdf_mcp-0.1.86.dist-info → lightpdf_aipdf_mcp-0.1.87.dist-info}/METADATA +1 -1
- lightpdf_aipdf_mcp-0.1.87.dist-info/RECORD +9 -0
- lightpdf_aipdf_mcp-0.1.86.dist-info/RECORD +0 -9
- {lightpdf_aipdf_mcp-0.1.86.dist-info → lightpdf_aipdf_mcp-0.1.87.dist-info}/WHEEL +0 -0
- {lightpdf_aipdf_mcp-0.1.86.dist-info → lightpdf_aipdf_mcp-0.1.87.dist-info}/entry_points.txt +0 -0
lightpdf_aipdf_mcp/converter.py
CHANGED
@@ -320,6 +320,13 @@ class Converter(BaseApiClient):
|
|
320
320
|
)
|
321
321
|
# 检查是否为URL路径
|
322
322
|
elif self.file_handler.is_url(file_path):
|
323
|
+
# arxiv.org/pdf/特殊处理
|
324
|
+
if isinstance(file_path, str) and "arxiv.org/pdf/" in file_path:
|
325
|
+
from urllib.parse import urlparse, urlunparse
|
326
|
+
url_obj = urlparse(file_path)
|
327
|
+
if not url_obj.path.endswith(".pdf"):
|
328
|
+
new_path = url_obj.path + ".pdf"
|
329
|
+
file_path = urlunparse(url_obj._replace(path=new_path))
|
323
330
|
data["url"] = file_path
|
324
331
|
# 使用JSON方式时添加Content-Type
|
325
332
|
headers["Content-Type"] = "application/json"
|
lightpdf_aipdf_mcp/editor.py
CHANGED
@@ -293,7 +293,7 @@ class Editor(BaseApiClient):
|
|
293
293
|
# 调用edit_pdf方法处理API请求
|
294
294
|
return await self.edit_pdf(file_path, EditType.DECRYPT, {}, password, original_name)
|
295
295
|
|
296
|
-
async def
|
296
|
+
async def add_text_watermark(
|
297
297
|
self,
|
298
298
|
file_path: str,
|
299
299
|
text: str,
|
@@ -359,6 +359,53 @@ class Editor(BaseApiClient):
|
|
359
359
|
# 调用edit_pdf方法处理API请求
|
360
360
|
return await self.edit_pdf(file_path, EditType.ADD_WATERMARK, extra_params, password, original_name)
|
361
361
|
|
362
|
+
async def add_image_watermark(
|
363
|
+
self,
|
364
|
+
file_path: str,
|
365
|
+
image_url: str,
|
366
|
+
position: str = "center",
|
367
|
+
opacity: float = 0.7,
|
368
|
+
range: str = "",
|
369
|
+
layout: Optional[str] = None,
|
370
|
+
password: Optional[str] = None,
|
371
|
+
original_name: Optional[str] = None
|
372
|
+
) -> EditResult:
|
373
|
+
"""为PDF文件添加图片水印
|
374
|
+
|
375
|
+
Args:
|
376
|
+
file_path: 要添加水印的PDF文件路径
|
377
|
+
image_url: 水印图片的URL,必须包含协议(http/https/oss)
|
378
|
+
position: 水印位置,如"top", "center", "diagonal"等,默认"center"
|
379
|
+
opacity: 透明度,0.0-1.0,默认0.7
|
380
|
+
range: 页面范围,例如 "1,3,5-7" 或空字符串表示所有页面
|
381
|
+
layout: 布局方式:"on"=在内容上,"under"=在内容下,默认"on"
|
382
|
+
password: 文档密码,如果文档受密码保护,则需要提供(可选)
|
383
|
+
original_name: 原始文件名(可选)
|
384
|
+
|
385
|
+
Returns:
|
386
|
+
EditResult: 添加图片水印结果
|
387
|
+
"""
|
388
|
+
# 验证输入文件是否为PDF
|
389
|
+
if not await self._validate_pdf_file(file_path):
|
390
|
+
return EditResult(success=False, file_path=file_path, error_message="非PDF文件", original_name=original_name)
|
391
|
+
if not image_url:
|
392
|
+
await self.logger.error("水印图片URL不能为空")
|
393
|
+
return EditResult(success=False, file_path=file_path, error_message="水印图片URL不能为空", original_name=original_name)
|
394
|
+
# 构建API参数
|
395
|
+
extra_params = {
|
396
|
+
"edit_type": "image",
|
397
|
+
"image_url": image_url,
|
398
|
+
"position": position,
|
399
|
+
"opacity": opacity,
|
400
|
+
"range": range
|
401
|
+
}
|
402
|
+
if layout:
|
403
|
+
extra_params["layout"] = layout
|
404
|
+
# 记录操作描述
|
405
|
+
await self._log_operation("为PDF添加图片水印", f"图片: {image_url}, 位置: {position}, 透明度: {opacity}")
|
406
|
+
# 调用edit_pdf方法处理API请求
|
407
|
+
return await self.edit_pdf(file_path, EditType.ADD_WATERMARK, extra_params, password, original_name)
|
408
|
+
|
362
409
|
async def remove_margin(self, file_path: str, password: Optional[str] = None, original_name: Optional[str] = None) -> EditResult:
|
363
410
|
"""去除PDF文件的白边
|
364
411
|
|
@@ -523,6 +570,13 @@ class Editor(BaseApiClient):
|
|
523
570
|
)
|
524
571
|
# 检查是否为URL路径
|
525
572
|
elif self.file_handler.is_url(file_path):
|
573
|
+
# arxiv.org/pdf/特殊处理
|
574
|
+
if isinstance(file_path, str) and "arxiv.org/pdf/" in file_path:
|
575
|
+
from urllib.parse import urlparse, urlunparse
|
576
|
+
url_obj = urlparse(file_path)
|
577
|
+
if not url_obj.path.endswith(".pdf"):
|
578
|
+
new_path = url_obj.path + ".pdf"
|
579
|
+
file_path = urlunparse(url_obj._replace(path=new_path))
|
526
580
|
# 使用JSON方式时添加Content-Type
|
527
581
|
headers["Content-Type"] = "application/json"
|
528
582
|
data["url"] = file_path
|
@@ -580,6 +634,13 @@ class Editor(BaseApiClient):
|
|
580
634
|
input_item["password"] = password
|
581
635
|
url_inputs.append(input_item)
|
582
636
|
elif self.file_handler.is_url(file_path):
|
637
|
+
# arxiv.org/pdf/特殊处理
|
638
|
+
if isinstance(file_path, str) and "arxiv.org/pdf/" in file_path:
|
639
|
+
from urllib.parse import urlparse, urlunparse
|
640
|
+
url_obj = urlparse(file_path)
|
641
|
+
if not url_obj.path.endswith(".pdf"):
|
642
|
+
new_path = url_obj.path + ".pdf"
|
643
|
+
file_path = urlunparse(url_obj._replace(path=new_path))
|
583
644
|
# 对于URL或OSS路径,添加到inputs数组
|
584
645
|
input_item = {"url": file_path}
|
585
646
|
if password:
|
lightpdf_aipdf_mcp/server.py
CHANGED
@@ -154,10 +154,10 @@ async def process_edit_file(
|
|
154
154
|
"""处理单个文件编辑"""
|
155
155
|
if edit_type == "decrypt":
|
156
156
|
return await editor.decrypt_pdf(file_path, password, original_name)
|
157
|
-
elif edit_type == "
|
158
|
-
return await editor.
|
157
|
+
elif edit_type == "add_text_watermark":
|
158
|
+
return await editor.add_text_watermark(
|
159
159
|
file_path=file_path,
|
160
|
-
text=extra_params.get("text", "
|
160
|
+
text=extra_params.get("text", "文本水印"),
|
161
161
|
position=extra_params.get("position", "center"),
|
162
162
|
opacity=extra_params.get("opacity", 1.0),
|
163
163
|
range=extra_params.get("range", ""),
|
@@ -168,6 +168,17 @@ async def process_edit_file(
|
|
168
168
|
password=password,
|
169
169
|
original_name=original_name
|
170
170
|
)
|
171
|
+
elif edit_type == "add_image_watermark":
|
172
|
+
return await editor.add_image_watermark(
|
173
|
+
file_path=file_path,
|
174
|
+
image_url=extra_params.get("image_url"),
|
175
|
+
position=extra_params.get("position", "center"),
|
176
|
+
opacity=extra_params.get("opacity", 0.7),
|
177
|
+
range=extra_params.get("range", ""),
|
178
|
+
layout=extra_params.get("layout", "on"),
|
179
|
+
password=password,
|
180
|
+
original_name=original_name
|
181
|
+
)
|
171
182
|
elif edit_type == "encrypt":
|
172
183
|
return await editor.encrypt_pdf(
|
173
184
|
file_path=file_path,
|
@@ -276,7 +287,8 @@ async def process_tool_call(
|
|
276
287
|
# 获取操作描述
|
277
288
|
edit_map = {
|
278
289
|
"decrypt": "解密",
|
279
|
-
"
|
290
|
+
"add_text_watermark": "添加文本水印",
|
291
|
+
"add_image_watermark": "添加图片水印",
|
280
292
|
"encrypt": "加密",
|
281
293
|
"compress": "压缩",
|
282
294
|
"split": "拆分",
|
@@ -509,7 +521,7 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
509
521
|
}
|
510
522
|
),
|
511
523
|
types.Tool(
|
512
|
-
name="
|
524
|
+
name="add_text_watermark",
|
513
525
|
description="Add text watermarks to PDF files.",
|
514
526
|
inputSchema={
|
515
527
|
"type": "object",
|
@@ -521,7 +533,7 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
521
533
|
"properties": {
|
522
534
|
"path": {
|
523
535
|
"type": "string",
|
524
|
-
"description": "PDF file URL to add watermark to, must include protocol, supports http/https/oss"
|
536
|
+
"description": "PDF file URL to add text watermark to, must include protocol, supports http/https/oss"
|
525
537
|
},
|
526
538
|
"password": {
|
527
539
|
"type": "string",
|
@@ -534,7 +546,7 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
534
546
|
},
|
535
547
|
"required": ["path"]
|
536
548
|
},
|
537
|
-
"description": "List of PDF files to add watermarks to, each containing path and optional password"
|
549
|
+
"description": "List of PDF files to add text watermarks to, each containing path and optional password"
|
538
550
|
},
|
539
551
|
"text": {
|
540
552
|
"type": "string",
|
@@ -542,7 +554,7 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
542
554
|
},
|
543
555
|
"position": {
|
544
556
|
"type": "string",
|
545
|
-
"description": "
|
557
|
+
"description": "Text watermark position: top-left(topleft), top-center(top), top-right(topright), left(left), center(center), right(right), bottom-left(bottomleft), bottom(bottom), bottom-right(bottomright), diagonal(diagonal, -45 degrees), reverse-diagonal(reverse-diagonal, 45 degrees)",
|
546
558
|
"enum": ["topleft", "top", "topright", "left", "center", "right",
|
547
559
|
"bottomleft", "bottom", "bottomright", "diagonal", "reverse-diagonal"],
|
548
560
|
"default": "center"
|
@@ -580,6 +592,66 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
580
592
|
"required": ["files", "text", "position"]
|
581
593
|
}
|
582
594
|
),
|
595
|
+
types.Tool(
|
596
|
+
name="add_image_watermark",
|
597
|
+
description="Add image watermarks to PDF files.",
|
598
|
+
inputSchema={
|
599
|
+
"type": "object",
|
600
|
+
"properties": {
|
601
|
+
"files": {
|
602
|
+
"type": "array",
|
603
|
+
"items": {
|
604
|
+
"type": "object",
|
605
|
+
"properties": {
|
606
|
+
"path": {
|
607
|
+
"type": "string",
|
608
|
+
"description": "PDF file URL to add image watermark to, must include protocol, supports http/https/oss"
|
609
|
+
},
|
610
|
+
"password": {
|
611
|
+
"type": "string",
|
612
|
+
"description": "PDF document password, required if the document is password-protected"
|
613
|
+
},
|
614
|
+
"name": {
|
615
|
+
"type": "string",
|
616
|
+
"description": "Original filename of the document"
|
617
|
+
}
|
618
|
+
},
|
619
|
+
"required": ["path"]
|
620
|
+
},
|
621
|
+
"description": "List of PDF files to add image watermarks to, each containing path and optional password"
|
622
|
+
},
|
623
|
+
"image_url": {
|
624
|
+
"type": "string",
|
625
|
+
"description": "Image URL for the watermark, must include protocol, supports http/https/oss"
|
626
|
+
},
|
627
|
+
"position": {
|
628
|
+
"type": "string",
|
629
|
+
"description": "Image watermark position: top-left(topleft), top-center(top), top-right(topright), left(left), center(center), right(right), bottom-left(bottomleft), bottom(bottom), bottom-right(bottomright), diagonal(diagonal, -45 degrees), reverse-diagonal(reverse-diagonal, 45 degrees)",
|
630
|
+
"enum": ["topleft", "top", "topright", "left", "center", "right",
|
631
|
+
"bottomleft", "bottom", "bottomright", "diagonal", "reverse-diagonal"],
|
632
|
+
"default": "center"
|
633
|
+
},
|
634
|
+
"opacity": {
|
635
|
+
"type": "number",
|
636
|
+
"description": "Opacity, 0.0-1.0",
|
637
|
+
"default": 0.7,
|
638
|
+
"minimum": 0.0,
|
639
|
+
"maximum": 1.0
|
640
|
+
},
|
641
|
+
"range": {
|
642
|
+
"type": "string",
|
643
|
+
"description": "Page range, e.g. '1,3,5-7' or '' (empty string or not set) for all pages"
|
644
|
+
},
|
645
|
+
"layout": {
|
646
|
+
"type": "string",
|
647
|
+
"description": "Layout position: on top of content(on) or under content(under)",
|
648
|
+
"enum": ["on", "under"],
|
649
|
+
"default": "on"
|
650
|
+
}
|
651
|
+
},
|
652
|
+
"required": ["files", "image_url", "position"]
|
653
|
+
}
|
654
|
+
),
|
583
655
|
types.Tool(
|
584
656
|
name="unlock_pdf",
|
585
657
|
description="Remove password protection from PDF files.",
|
@@ -984,11 +1056,16 @@ async def handle_call_tool(name: str, arguments: dict | None) -> list[types.Text
|
|
984
1056
|
"edit_type": "decrypt", # 编辑类型
|
985
1057
|
"is_edit_operation": True, # 标记为编辑操作
|
986
1058
|
},
|
987
|
-
"
|
988
|
-
"edit_type": "
|
1059
|
+
"add_text_watermark": {
|
1060
|
+
"edit_type": "add_text_watermark", # 编辑类型,文本水印
|
989
1061
|
"is_edit_operation": True, # 标记为编辑操作
|
990
1062
|
"param_keys": ["text", "position", "opacity", "range", "layout",
|
991
|
-
"font_family", "font_size", "font_color"] # 需要从arguments
|
1063
|
+
"font_family", "font_size", "font_color"] # 需要从arguments获取的参数(文本水印)
|
1064
|
+
},
|
1065
|
+
"add_image_watermark": {
|
1066
|
+
"edit_type": "add_image_watermark",
|
1067
|
+
"is_edit_operation": True,
|
1068
|
+
"param_keys": ["image_url", "position", "opacity", "range", "layout"]
|
992
1069
|
},
|
993
1070
|
"protect_pdf": {
|
994
1071
|
"edit_type": "encrypt", # 编辑类型
|
@@ -1075,8 +1152,8 @@ async def handle_call_tool(name: str, arguments: dict | None) -> list[types.Text
|
|
1075
1152
|
if name == "add_page_numbers":
|
1076
1153
|
# 添加页码工具使用"5"作为position默认值
|
1077
1154
|
operation_config["extra_params"][key] = arguments.get(key, DEFAULTS.get("position_page_numbers"))
|
1078
|
-
elif name == "
|
1079
|
-
#
|
1155
|
+
elif name == "add_text_watermark":
|
1156
|
+
# 添加文本水印工具使用"center"作为position默认值
|
1080
1157
|
operation_config["extra_params"][key] = arguments.get(key, DEFAULTS.get("position_watermark"))
|
1081
1158
|
else:
|
1082
1159
|
# 其他工具使用通用默认值
|
@@ -0,0 +1,9 @@
|
|
1
|
+
lightpdf_aipdf_mcp/__init__.py,sha256=PPnAgpvJLYLVOTxnHDmJAulFnHJD6wuTwS6tRGjqq6s,141
|
2
|
+
lightpdf_aipdf_mcp/common.py,sha256=_UO1f6S9Qr_3k6u5iBpdVDpvTK5U-tHEpu9KsDGqV8Y,6635
|
3
|
+
lightpdf_aipdf_mcp/converter.py,sha256=f0gS8tAQlJ8uwJUVUmd9nAA4O9m558e9lAT2B_MxmIo,15135
|
4
|
+
lightpdf_aipdf_mcp/editor.py,sha256=9teOqi2y2JbjcCI-kUhYpSXL-F75i7Mfr9E20KKyZP0,29909
|
5
|
+
lightpdf_aipdf_mcp/server.py,sha256=rn9QvHr5E-IaCx2V1pXuKtiXKf1-b3zns-VoxH6x2UE,56220
|
6
|
+
lightpdf_aipdf_mcp-0.1.87.dist-info/METADATA,sha256=4WUxVWAPL13KBXsEKfI3iHn4qBxof-GMBNVcXZtLmkQ,8119
|
7
|
+
lightpdf_aipdf_mcp-0.1.87.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
8
|
+
lightpdf_aipdf_mcp-0.1.87.dist-info/entry_points.txt,sha256=X7TGUe52N4sYH-tYt0YUGApeJgw-efQlZA6uAZmlmr4,63
|
9
|
+
lightpdf_aipdf_mcp-0.1.87.dist-info/RECORD,,
|
@@ -1,9 +0,0 @@
|
|
1
|
-
lightpdf_aipdf_mcp/__init__.py,sha256=PPnAgpvJLYLVOTxnHDmJAulFnHJD6wuTwS6tRGjqq6s,141
|
2
|
-
lightpdf_aipdf_mcp/common.py,sha256=_UO1f6S9Qr_3k6u5iBpdVDpvTK5U-tHEpu9KsDGqV8Y,6635
|
3
|
-
lightpdf_aipdf_mcp/converter.py,sha256=f9YuDtOmXBGlMmS3O4Xn3rdWljY9XcNxu0CjftH4s0o,14726
|
4
|
-
lightpdf_aipdf_mcp/editor.py,sha256=O7wF_HWs5l-IiXLbZYLNYjj1ygo2v4yGJEYMJtn7jpo,26916
|
5
|
-
lightpdf_aipdf_mcp/server.py,sha256=sHFc2c7gLM6qh5sqbZREynoT53QZDvoXKCNEzWfnC6o,52200
|
6
|
-
lightpdf_aipdf_mcp-0.1.86.dist-info/METADATA,sha256=jQKNGcg_UD18y2zd1SC4Mms3iGuE0bPW1lPp31tVw5Y,8119
|
7
|
-
lightpdf_aipdf_mcp-0.1.86.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
8
|
-
lightpdf_aipdf_mcp-0.1.86.dist-info/entry_points.txt,sha256=X7TGUe52N4sYH-tYt0YUGApeJgw-efQlZA6uAZmlmr4,63
|
9
|
-
lightpdf_aipdf_mcp-0.1.86.dist-info/RECORD,,
|
File without changes
|
{lightpdf_aipdf_mcp-0.1.86.dist-info → lightpdf_aipdf_mcp-0.1.87.dist-info}/entry_points.txt
RENAMED
File without changes
|