| name | render-publication-graphic |
| description | Produce publication-ready 2D graphics with proper DPI, color profiles, typography, and export formats for print and digital media. Use when preparing figures for academic journal submission, creating graphics for print publications, ensuring graphics meet publisher technical specifications, exporting visualizations for web with proper optimization, or creating multi-format exports from a single source.
|
| license | MIT |
| allowed-tools | Read Write Edit Bash Grep Glob |
| metadata | {"author":"Philipp Thoss","version":"1.1","domain":"visualization","complexity":"intermediate","language":"multi","tags":"publication, dpi, color-profile, typography, export, print, digital"} |
Render 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.
When to Use
- 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
Inputs
| 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 |
Procedure
1. Determine Output Requirements
Identify technical specifications for target publication:
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
Expected: Clear understanding of target requirements
On failure: 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)
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."""
width_inches = width_mm / 25.4
height_inches = height_mm / 25.4
width_px = int(width_inches * dpi)
height_px = int(height_inches * dpi)
return width_px, height_px
width, height = calculate_dimensions(180, 120, dpi=300)
print(f"Required resolution: {width}x{height} pixels")
library(ggplot2)
p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
theme_minimal(base_size = 12)
ggsave(
filename = "figure1.png",
plot = p,
width = 180,
height = 120,
units = "mm",
dpi = 300
)
ggsave(
filename = "figure1.pdf",
plot = p,
width = 180,
height = 120,
units = "mm",
device cairo_pdf
Expected: Graphics rendered at correct resolution for print quality
On failure: 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')
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 = ImageCms.createProfile('sRGB')
img_srgb = ImageCms.profileToProfile(
img,
srgb_profile,
srgb_profile,
renderingIntent=ImageCms.Intent.PERCEPTUAL
)
img_srgb.save(output_path)
convert input.png -colorspace sRGB output_srgb.png
convert input.png -colorspace CMYK output_cmyk.tiff
identify -verbose image.png | grep -i colorspace
Expected: Color space matches publication requirements
On failure: 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."""
fonts = {
'serif': 'Times New Roman',
'sans': 'Arial',
'mono': 'Courier New'
}
try:
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_specs = {
'minimum_font_size': '8pt',
'line_width_min': 0.5,
'panel_labels': {
'font': 'Arial Bold',
'size': '12pt',
'position': 'top-left',
'style': 'A, B, C'
},
: {
: ,
:
},
: {
: ,
: ,
:
}
}
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
legend.text element_textsize
panel.grid.minor element_blank
text element_textcolor
Expected: Text readable at publication size, fonts embedded properly
On failure: 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']:
for fmt in formats:
output = f"{base}.{fmt}"
if fmt == 'png':
cairosvg.svg2png(
url=source_path,
write_to=output,
output_width=2126,
output_height=1417
)
elif fmt == 'pdf':
cairosvg.svg2pdf(url=source_path, write_to=output)
elif fmt == 'tiff':
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:
img = Image.open(source_path)
for fmt in formats:
output =
fmt == :
img.save(output, =, dpi=(, ), optimize=)
fmt == :
img.save(output, =, compression=, dpi=(, ))
fmt == :
img.save(output, =, resolution=)
()
format_guide = {
: {
: ,
: ,
:
},
: {
: ,
: ,
:
},
: {
: ,
: ,
:
},
: {
: ,
: ,
:
},
: {
: ,
: ,
:
}
}
Expected: Appropriate format for publication channel
On failure: 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)
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)
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
img.save(output_path, format='JPEG', quality=quality, optimize=True, progressive=True)
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):
PIL Image
img = Image.(input_path)
sizes = [
(, ),
(, ),
(, )
]
width, suffix sizes:
img.width >= width:
ratio = width / img.width
height = (img.height * ratio)
resized = img.resize((width, height), Image.LANCZOS)
output =
resized.save(output, =, quality=, optimize=)
Expected: Web-optimized images under 500KB, responsive sizes generated
On failure: 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)
png_info = PngInfo()
for key, value in metadata.items():
png_info.add_text(key, str(value))
img.save(output_path, format='PNG', pnginfo=png_info)
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)
Expected: Metadata embedded and retrievable
On failure: Check format supports metadata (PNG, TIFF, PDF yes; JPEG limited)
Validation Checklist
Common Pitfalls
- 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
- Compression artifacts: JPEG compression unsuitable for line art or text
- Transparency issues: Some formats don't preserve transparency correctly
- Aspect ratio: Distortion from incorrect dimension calculations
Related Skills