feat: use colors in gdocs

This commit is contained in:
Olivier Schiavo
2025-11-27 11:29:25 +01:00
parent 252ebe482e
commit 7e2f9cdd96
4 changed files with 159 additions and 19 deletions

View File

@@ -9,12 +9,54 @@ from typing import Dict, Any, Optional, Tuple
logger = logging.getLogger(__name__)
def _normalize_color(color: Any, param_name: str) -> Optional[Dict[str, float]]:
"""
Normalize a user-supplied color into Docs API rgbColor format.
Supports:
- Hex strings: "#RRGGBB" or "RRGGBB"
- Tuple/list of 3 ints (0-255) or floats (0-1)
"""
if color is None:
return None
def _to_component(value: Any) -> float:
if isinstance(value, int):
if value < 0 or value > 255:
raise ValueError(f"{param_name} components must be 0-255 when using integers")
return value / 255
if isinstance(value, float):
if value < 0 or value > 1:
raise ValueError(f"{param_name} components must be between 0 and 1 when using floats")
return value
raise ValueError(f"{param_name} components must be int (0-255) or float (0-1)")
if isinstance(color, str):
hex_color = color.lstrip('#')
if len(hex_color) != 6 or any(c not in '0123456789abcdefABCDEF' for c in hex_color):
raise ValueError(f"{param_name} must be a hex string like '#RRGGBB'")
r = int(hex_color[0:2], 16) / 255
g = int(hex_color[2:4], 16) / 255
b = int(hex_color[4:6], 16) / 255
return {'red': r, 'green': g, 'blue': b}
if isinstance(color, (list, tuple)) and len(color) == 3:
r = _to_component(color[0])
g = _to_component(color[1])
b = _to_component(color[2])
return {'red': r, 'green': g, 'blue': b}
raise ValueError(f"{param_name} must be a hex string or RGB tuple/list")
def build_text_style(
bold: bool = None,
italic: bool = None,
underline: bool = None,
font_size: int = None,
font_family: str = None
font_family: str = None,
text_color: Any = None,
background_color: Any = None
) -> tuple[Dict[str, Any], list[str]]:
"""
Build text style object for Google Docs API requests.
@@ -25,6 +67,8 @@ def build_text_style(
underline: Whether text should be underlined
font_size: Font size in points
font_family: Font family name
text_color: Text color as hex string or RGB tuple/list
background_color: Background (highlight) color as hex string or RGB tuple/list
Returns:
Tuple of (text_style_dict, list_of_field_names)
@@ -51,6 +95,16 @@ def build_text_style(
if font_family is not None:
text_style['weightedFontFamily'] = {'fontFamily': font_family}
fields.append('weightedFontFamily')
if text_color is not None:
rgb = _normalize_color(text_color, "text_color")
text_style['foregroundColor'] = {'color': {'rgbColor': rgb}}
fields.append('foregroundColor')
if background_color is not None:
rgb = _normalize_color(background_color, "background_color")
text_style['backgroundColor'] = {'color': {'rgbColor': rgb}}
fields.append('backgroundColor')
return text_style, fields
@@ -121,7 +175,9 @@ def create_format_text_request(
italic: bool = None,
underline: bool = None,
font_size: int = None,
font_family: str = None
font_family: str = None,
text_color: Any = None,
background_color: Any = None
) -> Optional[Dict[str, Any]]:
"""
Create an updateTextStyle request for Google Docs API.
@@ -134,11 +190,15 @@ def create_format_text_request(
underline: Whether text should be underlined
font_size: Font size in points
font_family: Font family name
text_color: Text color as hex string or RGB tuple/list
background_color: Background (highlight) color as hex string or RGB tuple/list
Returns:
Dictionary representing the updateTextStyle request, or None if no styles provided
"""
text_style, fields = build_text_style(bold, italic, underline, font_size, font_family)
text_style, fields = build_text_style(
bold, italic, underline, font_size, font_family, text_color, background_color
)
if not text_style:
return None
@@ -318,4 +378,3 @@ def validate_operation(operation: Dict[str, Any]) -> Tuple[bool, str]:
return False, f"Missing required field: {field}"
return True, ""