From d322b9e5ef23b031eaa93164d74b598259e65676 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Sep 2025 17:22:20 +0000 Subject: [PATCH] Fix emoji rendering in PDF exports for Docker containers Co-authored-by: clayschaad <11883505+clayschaad@users.noreply.github.com> --- DOCKER_CONFIG.md | 26 +++++++++++++++++++- Dockerfile | 46 ++++++++++++++++++++++++++++++++++++ README.md | 2 ++ Services/PdfExportService.cs | 22 ++++++++++++++--- docker-compose.yml | 12 ++++++++++ 5 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/DOCKER_CONFIG.md b/DOCKER_CONFIG.md index 967b82f..9490db1 100644 --- a/DOCKER_CONFIG.md +++ b/DOCKER_CONFIG.md @@ -61,4 +61,28 @@ No migration needed - existing installations will automatically: ## File Format -Configuration files use standard JSON format and can be edited directly if needed. Changes take effect after application restart. \ No newline at end of file +Configuration files use standard JSON format and can be edited directly if needed. Changes take effect after application restart. + +## PDF Export with Emoji Support + +The Docker image includes Noto Color Emoji fonts to ensure proper rendering of emoji characters (🚂, 🌅, 🌆) in PDF exports. This addresses issues where emojis appear as empty boxes or don't render at all in containerized environments. + +### Troubleshooting PDF Emoji Issues + +If emojis still don't display correctly in PDFs: + +1. Ensure the container has emoji fonts installed: + ```bash + docker exec fc-list | grep -i emoji + ``` + +2. The fallback text format includes both emoji and descriptive text: + - `🌅 (Morning) 06:00-14:00` + - `🌆 (Afternoon) 14:00-22:00` + - `🚂 (Train) 07:30→08:15` + +3. For custom deployments, install emoji fonts manually: + ```dockerfile + RUN apt-get update && apt-get install -y fonts-noto-color-emoji fontconfig \ + && fc-cache -fv + ``` \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dfe4516 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,46 @@ +# Use the ASP.NET Core runtime as the base image +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base + +# Install emoji fonts for PDF generation +RUN apt-get update && apt-get install -y \ + fonts-noto-color-emoji \ + fontconfig \ + && rm -rf /var/lib/apt/lists/* \ + && fc-cache -fv + +WORKDIR /app +EXPOSE 5000 + +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src + +# Copy project files +COPY ["Server/ShiftScheduler.Server.csproj", "Server/"] +COPY ["Client/ShiftScheduler.Client.csproj", "Client/"] +COPY ["Shared/ShiftScheduler.Shared.csproj", "Shared/"] +COPY ["Services/ShiftScheduler.Services.csproj", "Services/"] + +# Restore dependencies +RUN dotnet restore "Server/ShiftScheduler.Server.csproj" + +# Copy everything else +COPY . . + +# Build the application +WORKDIR "/src/Server" +RUN dotnet build "ShiftScheduler.Server.csproj" -c Release -o /app/build + +# Publish stage +FROM build AS publish +RUN dotnet publish "ShiftScheduler.Server.csproj" -c Release -o /app/publish + +# Final stage +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . + +# Create config directory for persistent configuration +RUN mkdir -p /app/config + +ENTRYPOINT ["dotnet", "ShiftScheduler.Server.dll"] \ No newline at end of file diff --git a/README.md b/README.md index 426da14..f28915a 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,8 @@ services: - ASPNETCORE_ENVIRONMENT=Production ``` +**Note:** The Docker image includes emoji font support (Noto Color Emoji) to ensure proper rendering of emoji icons in PDF exports. If emojis don't display correctly in PDFs, ensure your Docker host has the necessary fonts installed. + See [Docker Configuration Guide](DOCKER_CONFIG.md) for detailed deployment instructions. ## Usage diff --git a/Services/PdfExportService.cs b/Services/PdfExportService.cs index ae81441..8b81961 100644 --- a/Services/PdfExportService.cs +++ b/Services/PdfExportService.cs @@ -2,6 +2,7 @@ using QuestPDF.Fluent; using QuestPDF.Helpers; using QuestPDF.Infrastructure; using System.Globalization; +using System.Runtime.InteropServices; using ShiftScheduler.Shared; namespace ShiftScheduler.Services @@ -14,6 +15,17 @@ namespace ShiftScheduler.Services static PdfExportService() { QuestPDF.Settings.License = LicenseType.Community; + + // Note: QuestPDF 2025.7.0 automatically handles most fonts + // The main issue is that Docker containers often lack emoji fonts + // This will be addressed via the Dockerfile + } + + private static string GetTextWithFallback(string textWithEmoji, string fallbackText) + { + // For PDF generation, we'll try emoji first but provide fallback text in parentheses + // This ensures the PDF is readable even when emoji fonts aren't available + return $"{textWithEmoji} ({fallbackText})"; } public byte[] GenerateMonthlySchedulePdf(List shiftsWithTransport) @@ -129,7 +141,9 @@ namespace ShiftScheduler.Services foreach (var day in Enumerable.Range(0, dayInWeek)) { var shiftWithTransport = shiftsWithTransport.GetValueOrDefault(weekDates.ElementAtOrDefault(day)); - var morningTime = shiftWithTransport?.Shift.MorningTime.Length > 0 ? $"🌅 {shiftWithTransport?.Shift.MorningTime}" : ""; + var morningTime = shiftWithTransport?.Shift.MorningTime.Length > 0 + ? GetTextWithFallback("🌅", "Morning") + $" {shiftWithTransport?.Shift.MorningTime}" + : ""; table.Cell().Element(CellStyleMiddle).Text(morningTime); } foreach (var day in Enumerable.Range(dayInWeek, 7 - dayInWeek)) @@ -141,7 +155,9 @@ namespace ShiftScheduler.Services foreach (var day in Enumerable.Range(0, dayInWeek)) { var shiftWithTransport = shiftsWithTransport.GetValueOrDefault(weekDates.ElementAtOrDefault(day)); - var afternoonTime = shiftWithTransport?.Shift.AfternoonTime.Length > 0 ? $"🌆 {shiftWithTransport?.Shift.AfternoonTime}" : ""; + var afternoonTime = shiftWithTransport?.Shift.AfternoonTime.Length > 0 + ? GetTextWithFallback("🌆", "Afternoon") + $" {shiftWithTransport?.Shift.AfternoonTime}" + : ""; table.Cell().Element(CellStyleMiddle).Text(afternoonTime); } foreach (var day in Enumerable.Range(dayInWeek, 7 - dayInWeek)) @@ -195,7 +211,7 @@ namespace ShiftScheduler.Services { var departure = transport.DepartureTime.ToString("HH:mm"); var arrival = transport.ArrivalTime.ToString("HH:mm"); - return $"🚂 {departure}→{arrival}"; + return GetTextWithFallback("🚂", "Train") + $" {departure}→{arrival}"; } private static void RenderEmptyCell(TableDescriptor table) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f8e68bb --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.8' +services: + shiftscheduler: + build: . + ports: + - "5000:5000" + volumes: + - ./config:/app/config # Persistent configuration + environment: + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://+:5000 + restart: unless-stopped \ No newline at end of file