Skip to main content
Glama

PowerPoint MCP Server

validation_utils.py11.8 kB
""" Validation utilities for PowerPoint MCP Server. Functions for validating and fixing slide content, text fit, and layouts. """ from typing import Dict, List, Optional, Any def validate_text_fit(shape, text_content: str = None, font_size: int = 12) -> Dict: """ Validate if text content will fit in a shape container. Args: shape: The shape containing the text text_content: The text to validate (if None, uses existing text) font_size: The font size to check Returns: Dictionary with validation results and suggestions """ result = { 'fits': True, 'estimated_overflow': False, 'suggested_font_size': font_size, 'suggested_dimensions': None, 'warnings': [], 'needs_optimization': False } try: # Use existing text if not provided if text_content is None and hasattr(shape, 'text_frame'): text_content = shape.text_frame.text if not text_content: return result # Basic heuristic: estimate if text will overflow if hasattr(shape, 'width') and hasattr(shape, 'height'): # Rough estimation: average character width is about 0.6 * font_size avg_char_width = font_size * 0.6 estimated_width = len(text_content) * avg_char_width # Convert shape dimensions to points (assuming they're in EMU) shape_width_pt = shape.width / 12700 # EMU to points conversion shape_height_pt = shape.height / 12700 if estimated_width > shape_width_pt: result['fits'] = False result['estimated_overflow'] = True result['needs_optimization'] = True # Suggest smaller font size suggested_size = int((shape_width_pt / len(text_content)) * 0.8) result['suggested_font_size'] = max(suggested_size, 8) # Suggest larger dimensions result['suggested_dimensions'] = { 'width': estimated_width * 1.2, 'height': shape_height_pt } result['warnings'].append( f"Text may overflow. Consider font size {result['suggested_font_size']} " f"or increase width to {result['suggested_dimensions']['width']:.1f} points" ) # Check for very long lines that might cause formatting issues lines = text_content.split('\n') max_line_length = max(len(line) for line in lines) if lines else 0 if max_line_length > 100: # Arbitrary threshold result['warnings'].append("Very long lines detected. Consider adding line breaks.") result['needs_optimization'] = True return result except Exception as e: result['fits'] = False result['error'] = str(e) return result def validate_and_fix_slide(slide, auto_fix: bool = True, min_font_size: int = 8, max_font_size: int = 72) -> Dict: """ Comprehensively validate and automatically fix slide content issues. Args: slide: The slide object to validate auto_fix: Whether to automatically apply fixes min_font_size: Minimum allowed font size max_font_size: Maximum allowed font size Returns: Dictionary with validation results and applied fixes """ result = { 'validation_passed': True, 'issues_found': [], 'fixes_applied': [], 'warnings': [], 'shapes_processed': 0, 'text_shapes_optimized': 0 } try: shapes_with_text = [] # Find all shapes with text content for i, shape in enumerate(slide.shapes): result['shapes_processed'] += 1 if hasattr(shape, 'text_frame') and shape.text_frame.text.strip(): shapes_with_text.append((i, shape)) # Validate each text shape for shape_index, shape in shapes_with_text: shape_name = f"Shape {shape_index}" # Validate text fit text_validation = validate_text_fit(shape, font_size=12) if not text_validation['fits'] or text_validation['needs_optimization']: issue = f"{shape_name}: Text may not fit properly" result['issues_found'].append(issue) result['validation_passed'] = False if auto_fix and text_validation['suggested_font_size']: try: # Apply suggested font size suggested_size = max(min_font_size, min(text_validation['suggested_font_size'], max_font_size)) # Apply font size to all runs in the text frame for paragraph in shape.text_frame.paragraphs: for run in paragraph.runs: if hasattr(run, 'font'): run.font.size = suggested_size * 12700 # Convert to EMU fix = f"{shape_name}: Adjusted font size to {suggested_size}pt" result['fixes_applied'].append(fix) result['text_shapes_optimized'] += 1 except Exception as e: warning = f"{shape_name}: Could not auto-fix font size: {str(e)}" result['warnings'].append(warning) # Check for other potential issues if len(shape.text_frame.text) > 500: # Very long text result['warnings'].append(f"{shape_name}: Contains very long text (>500 chars)") # Check for empty paragraphs empty_paragraphs = sum(1 for p in shape.text_frame.paragraphs if not p.text.strip()) if empty_paragraphs > 2: result['warnings'].append(f"{shape_name}: Contains {empty_paragraphs} empty paragraphs") # Check slide-level issues if len(slide.shapes) > 20: result['warnings'].append("Slide contains many shapes (>20), may affect performance") # Summary if result['validation_passed']: result['summary'] = "Slide validation passed successfully" else: result['summary'] = f"Found {len(result['issues_found'])} issues" if auto_fix: result['summary'] += f", applied {len(result['fixes_applied'])} fixes" return result except Exception as e: result['validation_passed'] = False result['error'] = str(e) return result def validate_slide_layout(slide) -> Dict: """ Validate slide layout for common issues. Args: slide: The slide object Returns: Dictionary with layout validation results """ result = { 'layout_valid': True, 'issues': [], 'suggestions': [], 'shape_count': len(slide.shapes), 'overlapping_shapes': [] } try: shapes = list(slide.shapes) # Check for overlapping shapes for i, shape1 in enumerate(shapes): for j, shape2 in enumerate(shapes[i+1:], i+1): if shapes_overlap(shape1, shape2): result['overlapping_shapes'].append({ 'shape1_index': i, 'shape2_index': j, 'shape1_name': getattr(shape1, 'name', f'Shape {i}'), 'shape2_name': getattr(shape2, 'name', f'Shape {j}') }) if result['overlapping_shapes']: result['layout_valid'] = False result['issues'].append(f"Found {len(result['overlapping_shapes'])} overlapping shapes") result['suggestions'].append("Consider repositioning overlapping shapes") # Check for shapes outside slide boundaries slide_width = 10 * 914400 # Standard slide width in EMU slide_height = 7.5 * 914400 # Standard slide height in EMU shapes_outside = [] for i, shape in enumerate(shapes): if (shape.left < 0 or shape.top < 0 or shape.left + shape.width > slide_width or shape.top + shape.height > slide_height): shapes_outside.append(i) if shapes_outside: result['layout_valid'] = False result['issues'].append(f"Found {len(shapes_outside)} shapes outside slide boundaries") result['suggestions'].append("Reposition shapes to fit within slide boundaries") # Check shape spacing if len(shapes) > 1: min_spacing = check_minimum_spacing(shapes) if min_spacing < 0.1 * 914400: # Less than 0.1 inch spacing result['suggestions'].append("Consider increasing spacing between shapes") return result except Exception as e: result['layout_valid'] = False result['error'] = str(e) return result def shapes_overlap(shape1, shape2) -> bool: """ Check if two shapes overlap. Args: shape1: First shape shape2: Second shape Returns: True if shapes overlap, False otherwise """ try: # Get boundaries left1, top1 = shape1.left, shape1.top right1, bottom1 = left1 + shape1.width, top1 + shape1.height left2, top2 = shape2.left, shape2.top right2, bottom2 = left2 + shape2.width, top2 + shape2.height # Check for overlap return not (right1 <= left2 or right2 <= left1 or bottom1 <= top2 or bottom2 <= top1) except: return False def check_minimum_spacing(shapes: List) -> float: """ Check minimum spacing between shapes. Args: shapes: List of shapes Returns: Minimum spacing found between shapes (in EMU) """ min_spacing = float('inf') try: for i, shape1 in enumerate(shapes): for shape2 in shapes[i+1:]: # Calculate distance between shape edges distance = calculate_shape_distance(shape1, shape2) min_spacing = min(min_spacing, distance) return min_spacing if min_spacing != float('inf') else 0 except: return 0 def calculate_shape_distance(shape1, shape2) -> float: """ Calculate distance between two shapes. Args: shape1: First shape shape2: Second shape Returns: Distance between shape edges (in EMU) """ try: # Get centers center1_x = shape1.left + shape1.width / 2 center1_y = shape1.top + shape1.height / 2 center2_x = shape2.left + shape2.width / 2 center2_y = shape2.top + shape2.height / 2 # Calculate center-to-center distance dx = abs(center2_x - center1_x) dy = abs(center2_y - center1_y) # Subtract half-widths and half-heights to get edge distance edge_distance_x = max(0, dx - (shape1.width + shape2.width) / 2) edge_distance_y = max(0, dy - (shape1.height + shape2.height) / 2) # Return minimum edge distance return min(edge_distance_x, edge_distance_y) except: return 0

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/GongRzhe/Office-PowerPoint-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server