Agent-almanac render-publication-graphic
git clone https://github.com/pjt222/agent-almanac
T=$(mktemp -d) && git clone --depth=1 https://github.com/pjt222/agent-almanac "$T" && mkdir -p ~/.claude/skills && cp -r "$T/i18n/zh-CN/skills/render-publication-graphic" ~/.claude/skills/pjt222-agent-almanac-render-publication-graphic-34091b && rm -rf "$T"
i18n/zh-CN/skills/render-publication-graphic/SKILL.mdRender Publication Graphic
Produce publication-ready graphics that meet technical requirements for academic journals, books, presentations, and web publication. Covers DPI requirements, color space management, typography best practices, file format selection, and metadata embedding.
适用场景
- Preparing figures for academic journal submission
- Creating graphics for print publications (books, magazines)
- Generating high-quality assets for presentations
- Exporting visualizations for web publication with proper optimization
- Ensuring graphics meet publisher technical specifications
- Archiving graphics with proper metadata
- Creating multi-format exports from single source
输入
| Input | Type | Description | Example |
|---|---|---|---|
| Source graphic | File/Data | Original visualization or artwork | SVG, R ggplot, Python matplotlib, Blender render |
| Publication target | Specification | Journal, web, print, presentation | Nature journal, IEEE paper, website |
| Technical requirements | Parameters | DPI, dimensions, color space, format | 300 DPI, 180mm width, CMYK, TIFF |
| Style guide | Document | Publisher typography and formatting rules | Font families, line widths, color palette |
| Metadata | Information | Title, author, date, copyright, description | Figure caption, license info |
步骤
1. Determine Output Requirements
Identify technical specifications for target publication:
# Common publication requirements academic_journal: dpi: 300-600 format: TIFF, EPS, PDF color_space: RGB or CMYK (check guidelines) max_width: 180mm (single column) or 390mm (double column) fonts: Embed or outline resolution_minimums: line_art: 1000 DPI halftone: 300 DPI combination: 600 DPI web_publication: dpi: 72-96 (retina: 144-192) format: PNG, WebP, SVG color_space: sRGB max_file_size: 200KB-500KB optimization: Compress, progressive loading presentation: dpi: 96-150 format: PNG, PDF, SVG color_space: RGB dimensions: 16:9 or 4:3 aspect ratio contrast: High contrast for projectors print_book: dpi: 300-600 format: TIFF, PDF/X color_space: CMYK bleed: 3-5mm beyond trim fonts: Embedded
预期结果: Clear understanding of target requirements 失败处理: Contact publisher for specific guidelines, use conservative defaults
2. Set Correct DPI for Raster Graphics
Configure resolution based on output medium:
from PIL import Image def set_dpi_pillow(image_path, output_path, target_dpi=300): """Set DPI metadata for PNG/TIFF.""" img = Image.open(image_path) # Save with DPI metadata img.save(output_path, dpi=(target_dpi, target_dpi)) print(f"Saved with {target_dpi} DPI: {output_path}") def calculate_dimensions(width_mm, height_mm, dpi=300): """Calculate pixel dimensions from physical size.""" # Convert mm to inches width_inches = width_mm / 25.4 height_inches = height_mm / 25.4 # Calculate pixels width_px = int(width_inches * dpi) height_px = int(height_inches * dpi) return width_px, height_px # Example: 180mm wide figure at 300 DPI width, height = calculate_dimensions(180, 120, dpi=300) print(f"Required resolution: {width}x{height} pixels") # Output: Required resolution: 2126x1417 pixels
# R ggplot2 export with proper DPI library(ggplot2) # Create plot p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + theme_minimal(base_size = 12) # Save for publication (300 DPI) ggsave( filename = "figure1.png", plot = p, width = 180, height = 120, units = "mm", dpi = 300 ) # Save as vector for flexibility ggsave( filename = "figure1.pdf", plot = p, width = 180, height = 120, units = "mm", device = cairo_pdf # Better text rendering )
预期结果: Graphics rendered at correct resolution for print quality 失败处理: Verify DPI metadata saved correctly, check file size appropriate
3. Configure Color Space
Set appropriate color profile:
from PIL import Image, ImageCms def convert_to_cmyk(rgb_image_path, cmyk_output_path): """Convert RGB to CMYK for print.""" img = Image.open(rgb_image_path) if img.mode != 'RGB': img = img.convert('RGB') # Convert to CMYK cmyk_img = img.convert('CMYK') cmyk_img.save(cmyk_output_path, format='TIFF', compression='tiff_lzw') print(f"Converted to CMYK: {cmyk_output_path}") def apply_srgb_profile(image_path, output_path): """Apply sRGB profile for web.""" img = Image.open(image_path) # sRGB profile (embedded in Pillow) srgb_profile = ImageCms.createProfile('sRGB') # Convert to sRGB img_srgb = ImageCms.profileToProfile( img, srgb_profile, srgb_profile, renderingIntent=ImageCms.Intent.PERCEPTUAL ) img_srgb.save(output_path)
# ImageMagick for color space conversion convert input.png -colorspace sRGB output_srgb.png convert input.png -colorspace CMYK output_cmyk.tiff # Check color profile identify -verbose image.png | grep -i colorspace
预期结果: Color space matches publication requirements 失败处理: Verify color profile embedded, test print preview
4. Configure Typography
Ensure text is readable and properly formatted:
from PIL import ImageFont def get_publication_fonts(): """Load fonts appropriate for publication.""" # Common publication-safe fonts fonts = { 'serif': 'Times New Roman', 'sans': 'Arial', 'mono': 'Courier New' } try: # Load with proper size for DPI # At 300 DPI, 12pt = 12 * 300/72 = 50 pixels base_size_300dpi = 50 font_regular = ImageFont.truetype(f"{fonts['sans']}.ttf", base_size_300dpi) font_bold = ImageFont.truetype(f"{fonts['sans']} Bold.ttf", base_size_300dpi) return {'regular': font_regular, 'bold': font_bold} except: return {'regular': ImageFont.load_default(), 'bold': ImageFont.load_default()} # Typography guidelines typography_specs = { 'minimum_font_size': '8pt', # Readable when printed 'line_width_min': 0.5, # Points, for print clarity 'panel_labels': { 'font': 'Arial Bold', 'size': '12pt', 'position': 'top-left', 'style': 'A, B, C' # Or (a), (b), (c) }, 'axis_labels': { 'font': 'Arial', 'size': '10pt' }, 'legend': { 'font': 'Arial', 'size': '9pt', 'position': 'outside plot area' } }
# R publication-quality typography library(ggplot2) p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point(size = 2) + labs( title = "Fuel Efficiency vs Weight", x = "Weight (1000 lbs)", y = "Miles per Gallon" ) + theme_bw(base_size = 12, base_family = "Arial") + theme( plot.title = element_text(size = 14, face = "bold"), axis.title = element_text(size = 12), axis.text = element_text(size = 10), legend.text = element_text(size = 10), panel.grid.minor = element_blank(), # Ensure text is black for print text = element_text(color = "black") )
预期结果: Text readable at publication size, fonts embedded properly 失败处理: Increase font sizes, check font licensing, convert text to outlines
5. Select Appropriate File Format
Choose format based on use case:
def export_multi_format(source_path, output_base, formats=['png', 'pdf', 'tiff']): """Export graphic in multiple formats.""" from PIL import Image import cairosvg import os base, ext = os.path.splitext(output_base) if ext.lower() in ['.svg']: # SVG source - convert to rasters for fmt in formats: output = f"{base}.{fmt}" if fmt == 'png': cairosvg.svg2png( url=source_path, write_to=output, output_width=2126, # 180mm @ 300 DPI output_height=1417 # 120mm @ 300 DPI ) elif fmt == 'pdf': cairosvg.svg2pdf(url=source_path, write_to=output) elif fmt == 'tiff': # Convert via PNG intermediate temp_png = f"{base}_temp.png" cairosvg.svg2png(url=source_path, write_to=temp_png) img = Image.open(temp_png) img.save(output, format='TIFF', compression='tiff_lzw') os.remove(temp_png) else: # Raster source img = Image.open(source_path) for fmt in formats: output = f"{base}.{fmt}" if fmt == 'png': img.save(output, format='PNG', dpi=(300, 300), optimize=True) elif fmt == 'tiff': img.save(output, format='TIFF', compression='tiff_lzw', dpi=(300, 300)) elif fmt == 'pdf': # Use img2pdf or similar for raster-to-PDF img.save(output, format='PDF', resolution=300.0) print(f"Exported in formats: {', '.join(formats)}") # Format selection guide format_guide = { 'TIFF': { 'use_for': 'Journal submission, archival', 'benefits': 'Lossless, supports CMYK, high quality', 'compression': 'LZW or ZIP (lossless)' }, 'PDF': { 'use_for': 'Submission, print, archival', 'benefits': 'Vector or raster, text searchable, widely accepted', 'variants': 'PDF/A (archival), PDF/X (print)' }, 'PNG': { 'use_for': 'Web, presentations, digital', 'benefits': 'Lossless, transparency, good compression', 'limitation': 'RGB only, larger than JPEG' }, 'SVG': { 'use_for': 'Web, further editing, scalable graphics', 'benefits': 'Vector, infinitely scalable, small file size', 'limitation': 'Not always accepted by journals' }, 'EPS': { 'use_for': 'Legacy journal requirements', 'benefits': 'Vector format accepted by older systems', 'limitation': 'Being phased out, use PDF instead' } }
预期结果: Appropriate format for publication channel 失败处理: Check publisher requirements, provide multiple formats
6. Optimize for Web
Create web-optimized versions:
def optimize_for_web(input_path, output_path, max_width=1200, quality=85): """Optimize image for web publication.""" from PIL import Image img = Image.open(input_path) # Resize if too large if img.width > max_width: ratio = max_width / img.width new_height = int(img.height * ratio) img = img.resize((max_width, new_height), Image.LANCZOS) # Convert to RGB if needed if img.mode in ('RGBA', 'LA', 'P'): background = Image.new('RGB', img.size, (255, 255, 255)) if img.mode == 'P': img = img.convert('RGBA') background.paste(img, mask=img.split()[-1] if 'A' in img.mode else None) img = background # Save optimized img.save(output_path, format='JPEG', quality=quality, optimize=True, progressive=True) # Check file size import os file_size_kb = os.path.getsize(output_path) / 1024 print(f"Optimized: {file_size_kb:.1f} KB") def create_responsive_set(input_path, output_base): """Create multiple resolutions for responsive web.""" from PIL import Image img = Image.open(input_path) sizes = [ (640, '640w'), (1024, '1024w'), (1920, '1920w') ] for width, suffix in sizes: if img.width >= width: ratio = width / img.width height = int(img.height * ratio) resized = img.resize((width, height), Image.LANCZOS) output = f"{output_base}_{suffix}.jpg" resized.save(output, format='JPEG', quality=85, optimize=True)
预期结果: Web-optimized images under 500KB, responsive sizes generated 失败处理: Reduce quality, resize further, consider WebP format
7. Embed Metadata
Add descriptive metadata for archival:
from PIL import Image from PIL.PngImagePlugin import PngInfo def embed_metadata(image_path, output_path, metadata): """Embed metadata in PNG.""" img = Image.open(image_path) # Create metadata png_info = PngInfo() for key, value in metadata.items(): png_info.add_text(key, str(value)) # Save with metadata img.save(output_path, format='PNG', pnginfo=png_info) # Example metadata metadata = { 'Title': 'Figure 1: Relationship between weight and fuel efficiency', 'Author': 'Jane Doe', 'Description': 'Scatter plot showing negative correlation', 'Copyright': 'CC-BY 4.0', 'Software': 'R 4.3.0, ggplot2 3.4.0', 'Creation Date': '2026-02-16', 'Source': 'mtcars dataset' } embed_metadata('figure1.png', 'figure1_with_metadata.png', metadata)
预期结果: Metadata embedded and retrievable 失败处理: Check format supports metadata (PNG, TIFF, PDF yes; JPEG limited)
Validation Checklist
- DPI meets publication requirements (typically 300+)
- Physical dimensions correct for publication
- Color space appropriate (RGB for web, CMYK for print)
- File format accepted by publisher
- Text is readable at publication size
- Fonts embedded or outlined
- Line widths visible when printed
- Color contrast sufficient for grayscale printing
- File size within limits
- Metadata embedded
- Tested print preview or rendering
常见问题
- Insufficient resolution: 72 DPI web graphics cannot be printed at quality
- Wrong color space: RGB graphics may print differently than displayed
- Font substitution: Non-embedded fonts replaced with defaults
- Small text: Fonts below 8pt may be illegible when printed
- Thin lines: Lines below 0.5pt may not print clearly
- File size: High DPI graphics can be very large, compress appropriately
- Compression artifacts: JPEG compression unsuitable for line art or text
- Missing bleed: Print graphics need 3-5mm bleed beyond trim
- Transparency issues: Some formats don't preserve transparency correctly
- Aspect ratio: Distortion from incorrect dimension calculations
相关技能
- create-2d-composition: Creating the source graphics
- render-blender-output: 3D rendering settings for publication
- generate-quarto-report: Integrating graphics into documents