{"spec_id":"subplot-grid","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nsubplot-grid: Subplot Grid Layout\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 81/100 | Updated: 2026-05-13\n\"\"\"\n\nimport os\nfrom io import BytesIO\n\nimport cairosvg\nimport numpy as np\nimport pygal\nfrom PIL import Image, ImageDraw, ImageFont\nfrom pygal.style import Style\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nBRAND = \"#009E73\"\nCOLOR_2 = \"#C475FD\"\n\n# Data - Business performance dashboard with multiple metrics\nnp.random.seed(42)\n\n# Monthly revenue trend (line chart)\nmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\nrevenue = [120, 135, 128, 145, 162, 158, 175, 189, 195, 210, 225, 248]  # in thousands\n\n# Sales by category (bar chart)\ncategories = [\"Electronics\", \"Apparel\", \"Home\", \"Sports\", \"Books\"]\nsales = [45.2, 32.8, 28.5, 19.7, 15.3]  # in thousands\n\n# Advertising spend vs ROI (scatter)\nad_spend = np.random.uniform(5, 50, 30)  # advertising spend in thousands\nroi = ad_spend * 0.12 + np.random.normal(0, 1.5, 30)  # ROI percentage\n\n# Daily order volume distribution (histogram)\ndaily_orders = np.random.normal(loc=150, scale=35, size=365)\ndaily_orders = np.clip(daily_orders, 50, 300)\nn_bins = 15\ncounts, bin_edges = np.histogram(daily_orders, bins=n_bins)\nhist_data = [(int(count), float(bin_edges[i]), float(bin_edges[i + 1])) for i, count in enumerate(counts)]\n\n# Chart styles\nline_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=(BRAND,),\n    font_family=\"sans-serif\",\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    stroke_width=3,\n    opacity=0.85,\n    opacity_hover=1.0,\n)\n\nbar_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=(COLOR_2,),\n    font_family=\"sans-serif\",\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    stroke_width=3,\n    opacity=0.85,\n    opacity_hover=1.0,\n)\n\nscatter_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=(BRAND,),\n    font_family=\"sans-serif\",\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    stroke_width=3,\n    opacity=0.85,\n    opacity_hover=1.0,\n)\n\nhist_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=(COLOR_2,),\n    font_family=\"sans-serif\",\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    stroke_width=3,\n    opacity=0.85,\n    opacity_hover=1.0,\n)\n\n# Create individual charts for the 2x2 grid\ncell_width = 2200\ncell_height = 1200\n\n# Top-left: Line chart - Monthly Revenue Trend\nline_chart = pygal.Line(\n    width=cell_width,\n    height=cell_height,\n    style=line_style,\n    title=\"Monthly Revenue ($K)\",\n    x_title=\"Month\",\n    y_title=\"Revenue ($K)\",\n    show_legend=False,\n    show_dots=True,\n    dots_size=10,\n    show_y_guides=True,\n    show_x_guides=False,\n    truncate_label=-1,\n)\nline_chart.x_labels = months\nline_chart.add(\"Revenue\", revenue)\n\n# Top-right: Bar chart - Sales by Category\nbar_chart = pygal.Bar(\n    width=cell_width,\n    height=cell_height,\n    style=bar_style,\n    title=\"Sales by Category ($K)\",\n    x_title=\"Category\",\n    y_title=\"Sales ($K)\",\n    show_legend=False,\n    print_values=False,\n    show_y_guides=True,\n    show_x_guides=False,\n    spacing=20,\n    truncate_label=-1,\n)\nbar_chart.x_labels = categories\nbar_chart.add(\"Sales\", sales)\n\n# Bottom-left: Scatter plot - Ad Spend vs ROI\nscatter_chart = pygal.XY(\n    width=cell_width,\n    height=cell_height,\n    style=scatter_style,\n    title=\"Ad Spend vs Return on Investment\",\n    x_title=\"Ad Spend ($K)\",\n    y_title=\"ROI (%)\",\n    show_legend=False,\n    stroke=False,\n    dots_size=12,\n    show_y_guides=True,\n    show_x_guides=True,\n)\nscatter_points = [(float(x), float(y)) for x, y in zip(ad_spend, roi, strict=True)]\nscatter_chart.add(\"Campaigns\", scatter_points)\n\n# Bottom-right: Histogram - Daily Order Distribution\nhist_chart = pygal.Histogram(\n    width=cell_width,\n    height=cell_height,\n    style=hist_style,\n    title=\"Daily Order Volume Distribution\",\n    x_title=\"Orders per Day\",\n    y_title=\"Frequency\",\n    show_legend=False,\n    show_y_guides=True,\n    show_x_guides=False,\n)\nhist_chart.add(\"Orders\", hist_data)\n\n# Render each chart to PNG\ncharts = [[line_chart, bar_chart], [scatter_chart, hist_chart]]\n\nimages = []\nfor row_charts in charts:\n    row_images = []\n    for chart in row_charts:\n        svg_bytes = chart.render()\n        png_bytes = cairosvg.svg2png(bytestring=svg_bytes, output_width=cell_width, output_height=cell_height)\n        img = Image.open(BytesIO(png_bytes))\n        row_images.append(img)\n    images.append(row_images)\n\n# Create combined image (4800 x 2700 with space for main title)\ntitle_height = 180\ntotal_width = 4800\ntotal_height = 2700\n\ncombined = Image.new(\"RGB\", (total_width, total_height), PAGE_BG)\n\n# Calculate grid positioning\ngrid_height = total_height - title_height\nmargin_x = 100\ngrid_width = total_width - 2 * margin_x\nactual_cell_width = grid_width // 2\nactual_cell_height = grid_height // 2\n\n# Paste charts into 2x2 grid\nfor row_idx, row_images in enumerate(images):\n    for col_idx, img in enumerate(row_images):\n        img_resized = img.resize((actual_cell_width, actual_cell_height), Image.LANCZOS)\n        x = margin_x + col_idx * actual_cell_width\n        y = title_height + row_idx * actual_cell_height\n        combined.paste(img_resized, (x, y))\n\n# Add main title using PIL\ndraw = ImageDraw.Draw(combined)\n\ntry:\n    title_font = ImageFont.truetype(\"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf\", 72)\nexcept OSError:\n    title_font = ImageFont.load_default()\n\ntitle_text = \"subplot-grid · pygal · anyplot.ai\"\nbbox = draw.textbbox((0, 0), title_text, font=title_font)\ntitle_width = bbox[2] - bbox[0]\ntitle_x = (total_width - title_width) // 2\ndraw.text((title_x, 50), title_text, fill=INK, font=title_font)\n\n# Save final PNG\ncombined.save(f\"plot-{THEME}.png\", dpi=(300, 300))\n\n# Create interactive HTML version\nhtml_content = f\"\"\"<!DOCTYPE html>\n<html>\n<head>\n    <title>subplot-grid · pygal · anyplot.ai</title>\n    <style>\n        body {{\n            font-family: sans-serif;\n            background: {PAGE_BG};\n            color: {INK};\n            margin: 20px;\n        }}\n        h1 {{\n            text-align: center;\n            color: {INK};\n            font-size: 32px;\n            margin-bottom: 30px;\n        }}\n        .grid {{\n            display: grid;\n            grid-template-columns: repeat(2, 1fr);\n            gap: 20px;\n            max-width: 1600px;\n            margin: 0 auto;\n        }}\n        .chart {{\n            width: 100%;\n            border: 1px solid {INK_MUTED};\n            border-radius: 8px;\n            overflow: hidden;\n        }}\n        .chart svg {{ width: 100%; height: auto; }}\n    </style>\n</head>\n<body>\n    <h1>subplot-grid · pygal · anyplot.ai</h1>\n    <div class=\"grid\">\n\"\"\"\n\n# Add all four charts\nall_charts = [line_chart, bar_chart, scatter_chart, hist_chart]\nfor chart in all_charts:\n    svg_data = chart.render(is_unicode=True)\n    svg_data = svg_data.replace('<?xml version=\"1.0\" encoding=\"utf-8\"?>', \"\")\n    html_content += f'        <div class=\"chart\">{svg_data}</div>\\n'\n\nhtml_content += \"\"\"    </div>\n</body>\n</html>\"\"\"\n\nwith open(f\"plot-{THEME}.html\", \"w\") as f:\n    f.write(html_content)\n"}