We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/sinjorjob/survey-insight-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""WordCloud画像生成モジュール."""
import logging
import random
from pathlib import Path
from typing import Optional
import matplotlib
import matplotlib.pyplot as plt
from wordcloud import WordCloud
# matplotlibのバックエンドをAggに設定(GUI不要)
matplotlib.use("Agg")
logger = logging.getLogger(__name__)
class WordCloudGenerator:
"""WordCloud画像生成を行うクラス.
日本語対応のWordCloudを生成し、美しいカラースキームを適用します。
"""
def __init__(self):
"""初期化."""
self.default_font = self._get_japanese_font()
self.color_schemes = {"accenture": self._accenture_color_func, "default": None}
def generate(
self,
text_or_frequencies: str | dict[str, int],
output_path: Optional[str] = None,
color_scheme: str = "accenture",
width: int = 1200,
height: int = 600,
) -> str:
"""WordCloudを生成.
Args:
text_or_frequencies: WordCloud生成元のテキストまたは頻度辞書
output_path: 画像出力パス(省略時は一時ファイル)
color_scheme: カラースキーム('accenture' or 'default')
width: 画像幅
height: 画像高さ
Returns:
str: 生成された画像のパス
Raises:
ValueError: データが空の場合
"""
# 入力チェック
if isinstance(text_or_frequencies, dict):
if not text_or_frequencies:
raise ValueError("頻度辞書が空です")
logger.info(f"WordCloud生成開始: {len(text_or_frequencies)}語の頻度辞書")
elif isinstance(text_or_frequencies, str):
if not text_or_frequencies or not text_or_frequencies.strip():
raise ValueError("テキストが空です")
logger.info(f"WordCloud生成開始: {len(text_or_frequencies)}文字")
else:
raise ValueError("テキストまたは頻度辞書を指定してください")
# カラー関数取得
color_func = self.color_schemes.get(color_scheme, None)
# WordCloud生成
wordcloud = WordCloud(
width=width,
height=height,
background_color="#F8F8F8" if color_scheme == "accenture" else "white",
font_path=self.default_font,
color_func=color_func,
prefer_horizontal=0.7,
relative_scaling=0.5,
min_font_size=10,
)
# 頻度辞書またはテキストから生成
if isinstance(text_or_frequencies, dict):
wordcloud.generate_from_frequencies(text_or_frequencies)
else:
wordcloud.generate(text_or_frequencies)
# 画像保存
if output_path is None:
output_path = "wordcloud.png"
output_file = Path(output_path)
output_file.parent.mkdir(parents=True, exist_ok=True)
plt.figure(figsize=(width / 100, height / 100), facecolor="#F8F8F8" if color_scheme == "accenture" else "white")
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")
plt.tight_layout(pad=0)
plt.savefig(
output_file, format="png", dpi=100, bbox_inches="tight", facecolor="#F8F8F8" if color_scheme == "accenture" else "white"
)
plt.close()
logger.info(f"WordCloud生成完了: {output_file}")
return str(output_file)
def _get_japanese_font(self) -> Optional[str]:
"""日本語フォントパスを取得.
Returns:
Optional[str]: フォントパス(見つからない場合はNone)
"""
# Windows環境での日本語フォント候補
font_candidates = [
"C:\\Windows\\Fonts\\msgothic.ttc", # MSゴシック
"C:\\Windows\\Fonts\\meiryo.ttc", # メイリオ
"C:\\Windows\\Fonts\\yugothm.ttc", # 游ゴシック
# Linux環境
"/usr/share/fonts/truetype/fonts-japanese-gothic.ttf",
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
# macOS環境
"/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc",
"/Library/Fonts/Hiragino Sans GB.ttc",
]
for font_path in font_candidates:
if Path(font_path).exists():
logger.info(f"日本語フォント検出: {font_path}")
return font_path
logger.warning("日本語フォントが見つかりません。デフォルトフォントを使用します。")
return None
def _accenture_color_func(self, word=None, font_size=None, position=None, orientation=None, font_path=None, random_state=None):
"""パープルグラデーションカラー関数.
パープルグラデーション(#A100FF → #6B00B6)のカラースキームを生成します。
Returns:
str: RGB文字列
"""
# パープルグラデーションの色リスト
purple_colors = [
(161, 0, 255), # #A100FF
(139, 0, 230),
(107, 0, 182), # #6B00B6
(201, 102, 255), # #C966FF (lighter)
(180, 50, 255),
]
# random_stateを使用して決定的に色を選択
# 単語のハッシュ値から色を決定(再現性を持たせる)
if word:
idx = hash(word) % len(purple_colors)
elif random_state is not None:
# numpy.random.RandomStateオブジェクトの場合
if hasattr(random_state, 'randint'):
idx = random_state.randint(0, len(purple_colors))
# 念のため範囲チェック
idx = min(idx, len(purple_colors) - 1)
else:
# 整数などの場合
idx = hash(random_state) % len(purple_colors)
else:
# random_stateがない場合はランダムに選択
idx = random.randint(0, len(purple_colors) - 1)
color = purple_colors[idx]
return f"rgb({color[0]}, {color[1]}, {color[2]})"