Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@
[icon]="'check-lg'"
(buttonClick)="image.id !== currentImageId && modal.close({ type: 'ImageSelected', image: image })" />
}
<span class="image-info small text-black text-nowrap tp-text-ellipsis mt-2" [title]="image.name">{{ image.name }}</span>
<span class="image-info small text-nowrap tp-text-ellipsis mt-2" [title]="image.name">{{ image.name }}</span>
<span
class="image-info xsmall text-black text-nowrap tp-text-ellipsis"
class="image-info xsmall text-nowrap tp-text-ellipsis"
[translate]="'Portal.ImageChooser.Dimensions'"
[translateParams]="{ w: image.width, h: image.height }"></span>
<span class="image-info xsmall text-black text-nowrap tp-text-ellipsis">{{ image.fileSize | fileSize: 'de' }}</span>
<span class="image-info xsmall text-nowrap tp-text-ellipsis">{{ image.fileSize | fileSize: 'de' }}</span>
</div>
<div class="image-wrapper d-flex flex-column align-items-center justify-content-center">
<img style="max-width: 9em" [src]="image.url" [alt]="image.name" (click)="hoverOverrideImageId = image.id" />
<img style="max-width: 9em; max-height: 9em" [src]="image.url" [alt]="image.name" (click)="hoverOverrideImageId = image.id" />
</div>
</div>
} @empty {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@
.image-tile {
&:hover,
&.hover-override {
border-color: #333 !important;

.hover-buttons {
background: rgb(255 255 255 / 85%);
display: flex !important;
z-index: 2;
}

.image-wrapper {
img {
opacity: 0.1;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<div class="d-flex flex-row flex-md-column align-items-center gap-3 gap-md-2">
@if (currentImage) {
<div class="border p-1 shadow-sm">
<div style="width: 10em; height: 10em" class="d-flex flex-column justify-content-center">
<img style="max-width: 10em" [src]="currentImage.url" [alt]="currentImage.name" />
<div style="width: 10em; height: 10em" class="d-flex flex-column align-items-center justify-content-center">
<img style="max-width: 10em; max-height: 10em" [src]="currentImage.url" [alt]="currentImage.name" />
</div>
</div>
} @else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,78 @@ namespace Turnierplan.PdfRendering.Extensions;

internal static class QuestPdfContainerExtensions
{
extension(IContainer container)
/// <summary>
/// Displays an image at the current position with the specified size. This method overwrites properties of the specified
/// <paramref name="container"/> so you should always call this method with a fresh container. Most importantly, the
/// <see cref="ElementExtensions.Unconstrained"/> method is called to allow for the desired positioning.
/// </summary>
public static void LogoImage(this IContainer container, Image image, IImageStorage imageStorage, float size, Unit sizeUnit, bool alignRight = false)
{
public void Image(Image image, IImageStorage imageStorage)
if (alignRight)
{
// Waiting for the task to complete is not ideal. However, attempting to use async
// inside the QuestPDF document structure is probably a much bigger nightmare...
var task = imageStorage.GetImageAsync(image);
task.Wait();
float translateX;

var stream = task.Result;
container.Image(stream);
if (image.Height > image.Width)
{
var imageDisplayWidth = (float)image.Width / image.Height * size;
translateX = -imageDisplayWidth;
}
else
{
translateX = -size;
}

// Dispose the stream (at this point, QuestPDF has read the stream content into an internal buffer)
stream.Dispose();
container = container.AlignRight().TranslateX(translateX, sizeUnit);
}

/// <remarks>
/// This code is taken from <see href="https://www.questpdf.com/api-reference/skiasharp-integration.html#helper-script"/>, last viewed on 2025-09-26
/// </remarks>
public void SkiaSharpSvgCanvas(Action<SKCanvas, Size> drawOnCanvas)
float translateY;

if (image.Width > image.Height)
{
container.Svg(size =>
{
using var stream = new MemoryStream();
var imageDisplayHeight = (float)image.Height / image.Width * size;
translateY = 0.5f * (size - imageDisplayHeight);
}
else
{
translateY = 0.0f;
}

using (var canvas = SKSvgCanvas.Create(new SKRect(0, 0, size.Width, size.Height), stream))
{
drawOnCanvas(canvas, size);
}
container.TranslateY(translateY, sizeUnit).Unconstrained().Width(size, sizeUnit).Height(size, sizeUnit).Image(image, imageStorage);
}

var svgData = stream.ToArray();
return Encoding.UTF8.GetString(svgData);
});
}
/// <summary>
/// Pulls the specified image from the configured image storage and creates a QuestPDF image with the retrieved image data.
/// </summary>
public static void Image(this IContainer container, Image image, IImageStorage imageStorage)
{
// Waiting for the task to complete is not ideal. However, attempting to use async
// inside the QuestPDF document structure is probably a much bigger nightmare...
var task = imageStorage.GetImageAsync(image);
task.Wait();

var stream = task.Result;
container.Image(stream);

// Dispose the stream (at this point, QuestPDF has read the stream content into an internal buffer)
stream.Dispose();
}

/// <remarks>
/// This code is taken from <see href="https://www.questpdf.com/api-reference/skiasharp-integration.html#helper-script"/>, last viewed on 2026-03-01
/// </remarks>
public static void SkiaSharpSvgCanvas(this IContainer container, Action<SKCanvas, Size> drawOnCanvas)
{
container.Svg(size =>
{
using var stream = new MemoryStream();

using (var canvas = SKSvgCanvas.Create(new SKRect(0, 0, size.Width, size.Height), stream))
{
drawOnCanvas(canvas, size);
}

var svgData = stream.ToArray();
return Encoding.UTF8.GetString(svgData);
});
}
}
4 changes: 2 additions & 2 deletions src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ string GetSectionHeader(string key)
if (tournament.PrimaryLogo is not null)
{
using var _ = DocumentRendererActivitySource.LoadRemoteImage(CurrentActivity, tournament.PrimaryLogo, nameof(tournament.PrimaryLogo));
column.Item().Unconstrained().Width(3, Unit.Centimetre).Image(tournament.PrimaryLogo, imageStorage);
column.Item().LogoImage(tournament.PrimaryLogo, imageStorage, 3.0f, Unit.Centimetre);
}

if (tournament.SecondaryLogo is not null)
{
using var _ = DocumentRendererActivitySource.LoadRemoteImage(CurrentActivity, tournament.SecondaryLogo, nameof(tournament.SecondaryLogo));
column.Item().AlignRight().Unconstrained().TranslateX(-3, Unit.Centimetre).Width(3, Unit.Centimetre).Image(tournament.SecondaryLogo, imageStorage);
column.Item().LogoImage(tournament.SecondaryLogo, imageStorage, 3.0f, Unit.Centimetre, alignRight: true);
}

var organizerName = string.IsNullOrWhiteSpace(configuration.OrganizerNameOverride)
Expand Down
4 changes: 2 additions & 2 deletions src/Turnierplan.PdfRendering/Renderer/ReceiptsRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ protected override Document Render(Tournament tournament, ReceiptsDocumentConfig
if (configuration.ShowPrimaryLogo && tournament.PrimaryLogo is not null)
{
using var _ = DocumentRendererActivitySource.LoadRemoteImage(CurrentActivity, tournament.PrimaryLogo, nameof(tournament.PrimaryLogo));
column.Item().Unconstrained().Width(1.7f, Unit.Centimetre).Image(tournament.PrimaryLogo, imageStorage);
column.Item().LogoImage(tournament.PrimaryLogo, imageStorage, 1.7f, Unit.Centimetre);
}

if (configuration.ShowSecondaryLogo && tournament.SecondaryLogo is not null)
{
using var _ = DocumentRendererActivitySource.LoadRemoteImage(CurrentActivity, tournament.SecondaryLogo, nameof(tournament.SecondaryLogo));
column.Item().AlignRight().Unconstrained().TranslateX(-1.7f, Unit.Centimetre).Width(1.7f, Unit.Centimetre).Image(tournament.SecondaryLogo, imageStorage);
column.Item().LogoImage(tournament.SecondaryLogo, imageStorage, 1.7f, Unit.Centimetre, alignRight: true);
}

column.Item().PaddingVertical(10).MinHeight(12, Unit.Millimetre).AlignMiddle().Column(headerColumn =>
Expand Down
Loading