def Font( self, size: int, font: str = "BurbankBigCondensed-Black.otf", directory: str = "assets/fonts/", ): """Return a font object with the specified font file and size.""" try: return ImageFont.truetype(f"{directory}{font}", size) except OSError: Log.Warn( self, "BurbankBigCondensed-Black.otf not found, defaulted font to LuckiestGuy-Regular.ttf", ) return ImageFont.truetype(f"{directory}LuckiestGuy-Regular.ttf", size) except Exception as e: Log.Error(self, f"Failed to load font, {e}")
def GenerateImage(self, date: str, itemShop: dict): """ Generate the Item Shop image using the provided Item Shop. Return True if image sucessfully saved. """ try: featured = itemShop["featured"] daily = itemShop["daily"] # Ensure both Featured and Daily have at least 1 item if (len(featured) <= 0) or (len(daily) <= 0): raise Exception( f"Featured: {len(featured)}, Daily: {len(daily)}") except Exception as e: Log.Error( self, f"Failed to parse Item Shop Featured and Daily items, {e}") return False # Determine the max amount of rows required for the current # Item Shop when there are 3 columns for both Featured and Daily. # This allows us to determine the image height. rows = max(ceil(len(featured) / 3), ceil(len(daily) / 3)) shopImage = Image.new("RGB", (1920, ((545 * rows) + 340))) try: background = ImageUtil.Open(self, "background.png") background = ImageUtil.RatioResize(self, background, shopImage.width, shopImage.height) shopImage.paste( background, ImageUtil.CenterX(self, background.width, shopImage.width)) except FileNotFoundError: Log.Warn(self, "Failed to open background.png, defaulting to dark gray") shopImage.paste((18, 18, 18), [0, 0, shopImage.size[0], shopImage.size[1]]) logo = ImageUtil.Open(self, "logo.png") logo = ImageUtil.RatioResize(self, logo, 0, 210) shopImage.paste( logo, ImageUtil.CenterX(self, logo.width, shopImage.width, 20), logo) canvas = ImageDraw.Draw(shopImage) font = ImageUtil.Font(self, 48) textWidth, _ = font.getsize(date) canvas.text( ImageUtil.CenterX(self, textWidth, shopImage.width, 255), date, (255, 255, 255), font=font, ) canvas.text((20, 255), "Featured", (255, 255, 255), font=font) textWidth, _ = font.getsize("Daily") canvas.text( (shopImage.width - (textWidth + 20), 255), "Daily", (255, 255, 255), font=font, ) # Track grid position i = 0 for item in featured: card = Athena.GenerateCard(self, item) if card is not None: shopImage.paste( card, ( (20 + ((i % 3) * (card.width + 5))), (315 + ((i // 3) * (card.height + 5))), ), card, ) i += 1 # Reset grid position i = 0 for item in daily: card = Athena.GenerateCard(self, item) if card is not None: shopImage.paste( card, ( (990 + ((i % 3) * (card.width + 5))), (315 + ((i // 3) * (card.height + 5))), ), card, ) i += 1 try: shopImage.save("itemshop.png") Log.Success(self, "Generated Item Shop image") return True except Exception as e: Log.Error(self, f"Failed to save Item Shop image, {e}")
def GenerateCard(self, item: dict): """Return the card image for the provided Fortnite Item Shop item.""" try: name = item["items"][0]["name"] rarity = item["items"][0]["rarity"] displayrarity = item["items"][0]["displayRarity"] category = item["items"][0]["type"] price = str(item["finalPrice"]) if (category == "outfit") or (category == "wrap"): if item["items"][0]["images"]["featured"] is not None: icon = item["items"][0]["images"]["featured"]["url"] else: icon = item["items"][0]["images"]["icon"]["url"] else: icon = item["items"][0]["images"]["icon"]["url"] except Exception as e: Log.Error(self, f"Failed to parse item {name}, {e}") return if rarity == "common": blendColor = (190, 190, 190) elif rarity == "uncommon": blendColor = (96, 170, 58) elif rarity == "rare": blendColor = (73, 172, 242) elif rarity == "epic": blendColor = (177, 91, 226) elif rarity == "legendary": blendColor = (211, 120, 65) elif rarity == "marvel": blendColor = (197, 51, 52) elif rarity == "dark": blendColor = (251, 34, 223) elif rarity == "dc": blendColor = (84, 117, 199) else: blendColor = (255, 255, 255) card = Image.new("RGBA", (300, 545)) try: layer = ImageUtil.Open(self, f"card_top_{rarity.lower()}.png") except FileNotFoundError: Log.Warn( self, f"Failed to open card_top_{rarity.lower()}.png, defaulted to Common", ) layer = ImageUtil.Open(self, "card_top_common.png") card.paste(layer) if category == 'glider': x = 285 / 1.1 y = 365 / 1.8 distanceTop = 60 elif category == 'music': x = 285 / 1.1 y = 365 / 1.6 distanceTop = 55 elif category == 'pickaxe': x = 285 / 1.1 y = 365 / 1.3 distanceTop = 40 elif category == 'wrap': x = 285 / 1.1 y = 365 / 1.3 distanceTop = 40 else: x = 285 y = 365 distanceTop = 10 icon = ImageUtil.Download(self, icon) icon = ImageUtil.RatioResize(self, icon, x, y) card.paste( icon, ImageUtil.CenterX(self, icon.width, card.width, distanceTop=distanceTop), icon) if len(item["items"]) > 1: # Track grid position i = 0 # Start at position 1 in items array for extra in item["items"][1:]: try: extraRarity = extra["rarity"] extraIcon = extra["images"]["smallIcon"]["url"] except Exception as e: Log.Error(self, f"Failed to parse item {name}, {e}") return try: layer = ImageUtil.Open( self, f"box_bottom_{extraRarity.lower()}.png") except FileNotFoundError: Log.Warn( self, f"Failed to open box_bottom_{extraRarity.lower()}.png, defaulted to Common", ) layer = ImageUtil.Open(self, "box_bottom_common.png") card.paste(layer, (17, (17 + ((i // 1) * (layer.height))))) extraIcon = ImageUtil.Download(self, extraIcon) extraIcon = ImageUtil.RatioResize(self, extraIcon, 75, 75) card.paste(extraIcon, (17, (17 + ((i // 1) * (extraIcon.height)))), extraIcon) try: layer = ImageUtil.Open( self, f"box_faceplate_{extraRarity.lower()}.png") except FileNotFoundError: Log.Warn( self, f"Failed to open box_faceplate_{extraRarity.lower()}.png, defaulted to Common", ) layer = ImageUtil.Open(self, "box_faceplate_common.png") card.paste(layer, (17, (17 + ((i // 1) * (layer.height)))), layer) i += 1 try: layer = ImageUtil.Open(self, f"card_faceplate_{rarity.lower()}.png") except FileNotFoundError: Log.Warn( self, f"Failed to open card_faceplate_{rarity.lower()}.png, defaulted to Common", ) layer = ImageUtil.Open(self, "card_faceplate_common.png") card.paste(layer, layer) try: layer = ImageUtil.Open(self, f"card_bottom_{rarity.lower()}.png") except FileNotFoundError: Log.Warn( self, f"Failed to open card_bottom_{rarity.lower()}.png, defaulted to Common", ) layer = ImageUtil.Open(self, "card_bottom_common.png") card.paste(layer, layer) canvas = ImageDraw.Draw(card) font = ImageUtil.Font(self, 30) textWidth, _ = font.getsize(f"{displayrarity} {category.title()}") canvas.text( ImageUtil.CenterX(self, textWidth, card.width, 385), f"{displayrarity} {category.title()}", blendColor, font=font, ) vbucks = ImageUtil.Open(self, "vbucks.png") vbucks = ImageUtil.RatioResize(self, vbucks, 25, 25) textWidth, _ = font.getsize(price) canvas.text( ImageUtil.CenterX(self, ((textWidth - 5) - vbucks.width), card.width, 495), price, (255, 255, 255), font=font, ) card.paste( vbucks, ImageUtil.CenterX(self, (vbucks.width + (textWidth + 5)), card.width, 495), vbucks, ) font = ImageUtil.Font(self, 56) textWidth, _ = font.getsize(name) if textWidth >= 270: # Ensure that the item name does not overflow font, textWidth = ImageUtil.FitTextX(self, name, 56, 265) canvas.text( ImageUtil.CenterX(self, textWidth, card.width, 425), name, (255, 255, 255), font=font, ) return card