@ww_nero/skills 2.2.2 → 2.2.4

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.
@@ -52,6 +52,12 @@
52
52
  <!-- Image Placeholder - circle (圆形头像占位) -->
53
53
  <circle id="image-placeholder-circle" cx="1150" cy="120" r="50" fill="rgba(255,255,255,0.1)" stroke="#00e5ff" stroke-width="2" stroke-dasharray="8 4"/>
54
54
 
55
+ <!-- Icon Placeholder (图标占位符,转换时会先保存为小PNG再插入) -->
56
+ <g id="icon-placeholder" transform="translate(1100, 650)">
57
+ <rect x="0" y="0" width="24" height="24" fill="none"/>
58
+ <path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke="#00e5ff" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
59
+ </g>
60
+
55
61
  <!-- Header Section -->
56
62
  <g transform="translate(60, 80)">
57
63
  <text x="0" y="0" font-family="'Segoe UI', Roboto, Helvetica, Arial, sans-serif" font-size="48" font-weight="bold" fill="#ffffff" letter-spacing="1">深度学习的崛起(1990-2017)</text>
@@ -2,9 +2,15 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  """
4
4
  将HTML文件中的SVG转换为PPTX
5
+
6
+ 注意事项:
7
+ - 对于 icon 元素(如带有 id="icon-*" 的 <g> 元素),
8
+ 会先将其独立保存为小的 PNG 图片,然后再插入到 PPTX 中。
9
+ 这样可以保证矢量图标在 PowerPoint 中正确显示。
5
10
  """
6
11
  import os
7
12
  import re
13
+ import tempfile
8
14
  from pptx import Presentation
9
15
  from pptx.util import Pt, Emu
10
16
  from pptx.dml.color import RGBColor
@@ -508,6 +514,68 @@ def add_image(slide, image_path, x, y, width, height):
508
514
  shape = slide.shapes.add_picture(image_path, px(x), px(y), px(width), px(height))
509
515
  return shape
510
516
 
517
+ def add_icon(slide, svg_content, x, y, width, height, scale=2):
518
+ """
519
+ 添加图标(先将SVG转为PNG再插入)
520
+
521
+ 对于 icon 元素,会先将其独立保存为小的 PNG 图片,然后再插入到 PPTX 中。
522
+ 这样可以保证矢量图标(如路径、复杂形状)在 PowerPoint 中正确显示。
523
+
524
+ 参数:
525
+ slide: 幻灯片对象
526
+ svg_content: SVG 内容字符串(完整的 SVG 标签或 <g> 元素内容)
527
+ x, y: 图标在幻灯片上的位置
528
+ width, height: 图标的显示尺寸
529
+ scale: 渲染时的放大倍数(提高清晰度,默认2倍)
530
+ """
531
+ try:
532
+ from playwright.sync_api import sync_playwright
533
+ from PIL import Image
534
+ import io
535
+
536
+ # 确保 svg_content 是完整的 SVG
537
+ if not svg_content.strip().startswith('<svg'):
538
+ svg_content = f'''<svg xmlns="http://www.w3.org/2000/svg"
539
+ width="{int(width * scale)}" height="{int(height * scale)}"
540
+ viewBox="0 0 {width} {height}">
541
+ {svg_content}
542
+ </svg>'''
543
+
544
+ # 使用 Playwright 渲染 SVG 为 PNG
545
+ with sync_playwright() as p:
546
+ browser = p.chromium.launch()
547
+ page = browser.new_page()
548
+
549
+ html_content = f'''<!DOCTYPE html>
550
+ <html>
551
+ <head>
552
+ <style>
553
+ body {{ margin: 0; padding: 0; background: transparent; }}
554
+ </style>
555
+ </head>
556
+ <body>{svg_content}</body>
557
+ </html>'''
558
+
559
+ page.set_content(html_content)
560
+ page.set_viewport_size({"width": int(width * scale), "height": int(height * scale)})
561
+
562
+ # 截图为 PNG
563
+ png_bytes = page.screenshot(type="png", omit_background=True)
564
+ browser.close()
565
+
566
+ # 插入图片到幻灯片
567
+ buffer = io.BytesIO(png_bytes)
568
+ shape = slide.shapes.add_picture(buffer, px(x), px(y), px(width), px(height))
569
+ return shape
570
+
571
+ except ImportError:
572
+ print("警告: 需要安装 playwright 和 PIL 来支持 icon 转换")
573
+ print("请运行: pip install playwright Pillow && playwright install chromium")
574
+ return None
575
+ except Exception as e:
576
+ print(f"警告: icon 转换失败: {e}")
577
+ return None
578
+
511
579
  def add_circular_image(slide, image_path, cx, cy, r):
512
580
  """添加圆形裁剪图片"""
513
581
  from PIL import Image, ImageDraw
@@ -634,6 +702,11 @@ def create_slide(prs):
634
702
  add_circle(slide, 1216, 696, 2, "#00e5ff", opacity=0.5)
635
703
  add_circle(slide, 1226, 696, 2, "#00e5ff", opacity=0.5)
636
704
 
705
+ # Icon 元素示例:复杂的 SVG 图标先转为小 PNG 再插入
706
+ icon_svg = '''<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"
707
+ stroke="#00e5ff" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>'''
708
+ add_icon(slide, icon_svg, 1100, 650, 24, 24)
709
+
637
710
  def main():
638
711
  prs = Presentation()
639
712
  prs.slide_width = px(1280)
package/index.js CHANGED
@@ -61,6 +61,7 @@ const SNIPPETS = {
61
61
  { label: '示例 HTML(ppt_slide.html)', file: 'ppt_slide.html', language: 'html' }
62
62
  ],
63
63
  notes: [
64
+ '【Icon 元素处理】对于复杂的 SVG 图标(如 <g id="icon-*"> 元素或包含 path 的小图标),应先将其独立保存为小的 PNG 图片,再插入到 PPTX 中。使用 add_icon(slide, svg_content, x, y, width, height) 函数,它会通过 Playwright 渲染 SVG 并以 2 倍分辨率截图,确保图标在 PowerPoint 中清晰显示。',
64
65
  '【文本颜色丢失】python-pptx 设置字体名称时会添加 a:latin/a:ea 等元素,如果颜色填充放在 rPr 末尾会被 PowerPoint 忽略。解决:用 rPr.insert(0, solidFill/gradFill) 将颜色填充插入到 rPr 开头,确保优先级最高。',
65
66
  '【渐变文本失效】设置渐变前必须先清理 a:solidFill、a:gradFill、a:noFill 三种标签,否则会冲突。同时添加 rotWithShape="1" 和 scaled="1" 属性确保渐变方向正确。',
66
67
  '【背景渐变被覆盖】直接操作 slide._element 时,若 p:bg 已存在需先 clear() 再重建,否则新旧元素混杂导致渲染异常。用 cSld.insert(0, bg) 确保背景节点在正确位置。',
@@ -85,9 +86,9 @@ const GUIDES = {
85
86
  - 如果用户未明确声明,则**默认比例为宽1280px、高720px,默认背景为深邃蓝色、科技感、弱渐变、简约风格**
86
87
  - 不同页面应尽可能使用不同的布局方式,避免过于单调,可适当使用图表来呈现信息,例如折线图、柱状图、饼状图等,但背景颜色保持一致
87
88
  - 如果需要插入图片,则按照以下方式:
88
- - 收集全部图片素材,优先使用用户提供的图片;若未提供,则使用网络搜索合适的图片或图标并保存到本地;仍无合适的,则使用图像生成工具生成图片
89
+ - 收集全部图片素材,优先使用用户提供的图片;若未提供,则优先使用网络搜索合适的图片或图标并保存到本地;仍无合适的,则使用图像生成工具生成图片
89
90
  - 将图片素材保存到\`images\`文件夹中,并使用临时python脚本获取每个图片的宽高比例
90
- - 对图片素材进行重命名来编号,通过名称标识每个图片用于哪个slide及其宽高比例,例如\`slide01_1_1920X1080.png\`表示该图片用于第一张幻灯片,并且是该幻灯片中的第一张图,宽高为1920*1080
91
+ - 对图片素材进行重命名来编号,通过名称标识每个图片用于哪个slide及其宽高比例,例如\`slide_1_1_16_9.png\`表示该图片为第1张Slide中的第1张图,宽高比为16:9
91
92
 
92
93
  2. **逐页生成SVG代码**:
93
94
  - 对于插图,统一使用\`rect\`或\`circle\`元素进行占位(必须符合图片宽高比例),之后转化成pptx时,在python脚本中换成真实的本地图片
@@ -196,7 +197,7 @@ const listTools = () => ({
196
197
  ]
197
198
  });
198
199
 
199
- const server = new Server({ name: 'skills', version: '2.2.2' }, { capabilities: { tools: {} } });
200
+ const server = new Server({ name: 'skills', version: '2.2.4' }, { capabilities: { tools: {} } });
200
201
 
201
202
  server.setRequestHandler(ListToolsRequestSchema, async () => listTools());
202
203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ww_nero/skills",
3
- "version": "2.2.2",
3
+ "version": "2.2.4",
4
4
  "description": "MCP server that returns Python reference snippets and skill guides",
5
5
  "main": "index.js",
6
6
  "bin": {