diff --git a/LWCProto.py b/LWCProto.py index a502b70..67cba06 100644 --- a/LWCProto.py +++ b/LWCProto.py @@ -138,8 +138,9 @@ def render_single_cards( card_matrix = layout.get_single_card_matrix(single_dpi) ctx.set_matrix(card_matrix) layout.clip_card(ctx) - ctx.set_source_rgb(1, 1, 1) - ctx.paint() + if card.imageFullFrame: + ctx.set_source_rgb(1, 1, 1) + ctx.paint() if handle_images and card.imageFullFrame: _require_image_helpers(load_full_frame_surface_fn) diff --git a/README.md b/README.md index 33ccc99..25256bd 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ El archivo de definicion de cartas es un archivo en jormato json con el siguente "text": "str", "colour": "#RRGGBB" }, + "background_color": "#RRGGBB", "footer": { "text": "str", "color": "#RRGGBB", @@ -80,6 +81,7 @@ El archivo de definicion de cartas es un archivo en jormato json con el siguente ``` El objeto `header` define el texto visible en la parte superior de la carta. El campo `color` ajusta el color del texto, mientras que los campos `banner` y `banner_color` permiten activar un recuadro de color sólido detrás del encabezado cuando sea necesario. El bloque `card_text` permite especificar el texto del cuerpo y el color con el que debe renderizarse. Para las imágenes puedes indicar un nombre de archivo directamente o un objeto con las claves `source` y `full_frame`. Cuando `full_frame_image` (o `full_frame` en el objeto de imagen) es `true`, la ilustración se ampliará para cubrir toda la carta; en caso contrario se mantendrá dentro del marco de arte. +Puedes controlar el color de fondo del lienzo con el campo opcional `background_color`. Debe indicarse en formato hexadecimal (`#RRGGBB`) y solo se aplica cuando la carta no utiliza una imagen a pantalla completa (`full_frame_image: false`). El bloque `footer` es opcional y permite mostrar una nota en la parte inferior de la carta. Puedes personalizar el texto, su color y el estilo de fuente (`normal`, `negrita` o `itálica`). Si no se especifica `font_style`, se utilizará `normal` por defecto. Las imagenes deben almacenarse en el directorio "images" que se encuentra en la misma carpeta que LWCProto.py, el formato de las imagenes es indiferente y su tamaño tambien estas seran redimensionadas automaticamente para adaptarse al tamaño disponible en el layout. Puedes utilizar el argumento `--output-dir` para indicar otro directorio base donde almacenar las cartas generadas, lo que facilita mantener varios prototipos separados. diff --git a/card_model.py b/card_model.py index 55a2afe..e0a60b9 100644 --- a/card_model.py +++ b/card_model.py @@ -37,6 +37,7 @@ def __init__(self, name=None, db=None): self.footerText = "" self.footerColour = "#000000" self.footerFontStyle = "normal" + self.backgroundColour = "#FFFFFF" if (name is not None) and (db is not None): # self.load(db[name][0]) For magic AllCards need this index @@ -102,6 +103,8 @@ def load(self, data: dict): self.imageFullFrame = image_full_frame + self.backgroundColour = data.get('background_color', '#FFFFFF') or '#FFFFFF' + footer = data.get('footer') or {} self.footerText = footer.get('text', '') or '' self.footerColour = footer.get('color', '#000000') or '#000000' @@ -129,6 +132,9 @@ def get_header_banner_color_rgb(self): def get_footer_text_color_rgb(self): return self._hex_to_rgb(self.footerColour, default=(0.0, 0.0, 0.0)) + def get_background_color_rgb(self): + return self._hex_to_rgb(self.backgroundColour, default=(1.0, 1.0, 1.0)) + @staticmethod def _hex_to_rgb(colour: str, *, default): value = (colour or '').strip() diff --git a/cartas.json b/cartas.json index 8237c5a..cdaa391 100644 --- a/cartas.json +++ b/cartas.json @@ -12,6 +12,7 @@ "text": "este es un texto de prueba", "colour": "#000000" }, + "background_color": "#F5F5F5", "footer": { "text": "footer carta 1", "color": "#333333" @@ -32,6 +33,7 @@ "text": "este es un texto de prueba para esta supercarta", "colour": "#FFFFFF" }, + "background_color": "#002244", "footer": { "text": "footer destacado", "color": "#FFD700", diff --git a/draw_card.py b/draw_card.py index 0db1489..2da4b45 100644 --- a/draw_card.py +++ b/draw_card.py @@ -45,6 +45,10 @@ def drawCard( ctx.save() layout.clip_card(ctx) + if not card.imageFullFrame: + ctx.set_source_rgb(*card.get_background_color_rgb()) + ctx.paint() + ctx.select_font_face('serif') diff --git a/tests/test_card_background.py b/tests/test_card_background.py new file mode 100644 index 0000000..eb003a5 --- /dev/null +++ b/tests/test_card_background.py @@ -0,0 +1,57 @@ +import pytest + +try: + import cairo # type: ignore +except Exception: # pragma: no cover - optional dependency missing + cairo = None + +from card_model import CardModel + +if cairo is not None: # pragma: no branch - conditional import for optional dependency + import layout + from draw_card import drawCard +else: # pragma: no cover - only triggered when cairo is missing + layout = None + drawCard = None + + +class TrackingCard(CardModel): + def __init__(self): + super().__init__() + self.background_calls = 0 + + def get_background_color_rgb(self): + self.background_calls += 1 + return super().get_background_color_rgb() + + +def _render_card(card: TrackingCard) -> TrackingCard: + assert cairo is not None and layout is not None and drawCard is not None + dpi = layout.SINGLE_CARD_DPI + surface = layout.get_single_card_surface(dpi) + ctx = cairo.Context(surface) + ctx.set_matrix(layout.get_single_card_matrix(dpi)) + drawCard(card, ctx) + return card + + +@pytest.mark.skipif(cairo is None, reason="pycairo is not available") +def test_draw_card_requests_background_colour_when_not_full_frame(): + card = TrackingCard() + card.backgroundColour = '#FF00FF' + card.imageFullFrame = False + + rendered = _render_card(card) + + assert rendered.background_calls >= 1 + + +@pytest.mark.skipif(cairo is None, reason="pycairo is not available") +def test_draw_card_skips_background_when_full_frame(): + card = TrackingCard() + card.backgroundColour = '#FF00FF' + card.imageFullFrame = True + + rendered = _render_card(card) + + assert rendered.background_calls == 0 diff --git a/tests/test_card_model.py b/tests/test_card_model.py index b502974..462bb99 100644 --- a/tests/test_card_model.py +++ b/tests/test_card_model.py @@ -26,6 +26,7 @@ def test_load_with_optional_fields(self): "toughness": 3, "image": "wizard.png", "full_frame_image": True, + "background_color": "#123456", } card = CardModel() @@ -47,6 +48,8 @@ def test_load_with_optional_fields(self): self.assertEqual(card.toughness, 3) self.assertEqual(card.image, "wizard.png") self.assertTrue(card.imageFullFrame) + self.assertEqual(card.backgroundColour, "#123456") + self.assertEqual(card.get_background_color_rgb(), (0x12 / 255.0, 0x34 / 255.0, 0x56 / 255.0)) def test_load_with_defaults(self): data = { @@ -74,6 +77,8 @@ def test_load_with_defaults(self): self.assertIsNone(card.toughness) self.assertIsNone(card.image) self.assertFalse(card.imageFullFrame) + self.assertEqual(card.backgroundColour, "#FFFFFF") + self.assertEqual(card.get_background_color_rgb(), (1.0, 1.0, 1.0)) def test_load_supports_image_object(self): data = {