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.
@@ -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"
@@ -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 add_watermark(
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:
@@ -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 == "add_watermark":
158
- return await editor.add_watermark(
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
- "add_watermark": "添加水印",
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="add_watermark",
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": "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)",
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
- "add_watermark": {
988
- "edit_type": "add_watermark", # 编辑类型
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 == "add_watermark":
1079
- # 添加水印工具使用"center"作为position默认值
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
  # 其他工具使用通用默认值
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lightpdf-aipdf-mcp
3
- Version: 0.1.86
3
+ Version: 0.1.87
4
4
  Summary: MCP Server for LightPDF AI-PDF
5
5
  Author: LightPDF Team
6
6
  License: Proprietary
@@ -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,,