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
2 changes: 1 addition & 1 deletion Caly.Core/Services/Interfaces/IPdfService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public interface IPdfService : IAsyncDisposable
/// <returns>The number of pages in the opened document. <c>0</c> if the document was not opened.</returns>
Task<int> OpenDocument(IStorageFile? storageFile, string? password, CancellationToken token);

ValueTask SetDocumentPropertiesAsync(DocumentViewModel document, CancellationToken token);
Task SetDocumentPropertiesAsync(DocumentViewModel document, CancellationToken token);

Task SetPdfBookmark(DocumentViewModel document, CancellationToken token);

Expand Down
88 changes: 86 additions & 2 deletions Caly.Core/Services/PdfPigPdfService.Lock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ namespace Caly.Core.Services
{
internal partial class PdfPigPdfService
{
private long _isDisposed;
private int _activeOperations;

// PdfPig only allow to read 1 page at a time for now
// NB: Initial count set to 0 to make sure the document is opened before anything else starts.
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0, 1);

private async Task<T?> ExecuteWithLockAsync<T>(Func<T> action, CancellationToken token)
private async Task<T?> ExecuteWithLockAsync<T>(Func<CancellationToken, T> action, CancellationToken token)
{
token.ThrowIfCancellationRequested();
if (IsDisposed())
Expand All @@ -50,7 +53,7 @@ internal partial class PdfPigPdfService
}

token.ThrowIfCancellationRequested();
return action();
return action(token);
}
finally
{
Expand All @@ -60,5 +63,86 @@ internal partial class PdfPigPdfService
}
}
}

private async Task GuardDispose(Func<CancellationToken, Task> action, CancellationToken token)
{
Interlocked.Increment(ref _activeOperations);
try
{
if (IsDisposed())
{
return;
}

using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(token, _mainCts.Token))
{
linkedCts.Token.ThrowIfCancellationRequested();
await action(linkedCts.Token);
}
}
catch (OperationCanceledException)
{ }
finally
{
Interlocked.Decrement(ref _activeOperations);
}
}

private async Task<T?> GuardDispose<T>(Func<CancellationToken, Task<T>> action, CancellationToken token)
{
Interlocked.Increment(ref _activeOperations);
try
{
if (IsDisposed())
{
return default;
}

using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(token, _mainCts.Token))
{
linkedCts.Token.ThrowIfCancellationRequested();
return await action(linkedCts.Token);
}
}
catch (OperationCanceledException)
{ }
finally
{
Interlocked.Decrement(ref _activeOperations);
}

return default;
}

private T? GuardDispose<T>(Func<CancellationToken, T> action, CancellationToken token)
{
Interlocked.Increment(ref _activeOperations);
try
{
if (IsDisposed())
{
return default;
}

using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(token, _mainCts.Token))
{
linkedCts.Token.ThrowIfCancellationRequested();
return action(linkedCts.Token);
}
}
catch (OperationCanceledException)
{ }
finally
{
Interlocked.Decrement(ref _activeOperations);
}

return default;
}

private bool IsDisposed()
{
return Interlocked.Read(ref _isDisposed) != 0;
}
}
}
108 changes: 60 additions & 48 deletions Caly.Core/Services/PdfPigPdfService.Pictures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,84 +34,96 @@ internal sealed partial class PdfPigPdfService
{
Debug.ThrowOnUiThread();

SKPicture? pic = await ExecuteWithLockAsync(() =>
SKPicture? pic = await GuardDispose(async ct =>
{
return await ExecuteWithLockAsync(lct =>
{
try
{
if (IsDisposed())
{
return null;
}

return _document?.GetPage<SKPicture>(pageNumber);
}
catch (OperationCanceledException)
{
throw; // No error picture to generate
}
catch (Exception e)
{
Debug.WriteExceptionToFile(e);
return GetErrorPicture(pageNumber, e, token);
return GetErrorPicture(pageNumber, e, lct);
}
},
token)
.ConfigureAwait(false);
}, ct)
.ConfigureAwait(false);
}, token);

return pic is null ? null : RefCountable.Create(pic);
}

private SKPicture? GetErrorPicture(int pageNumber, Exception ex, CancellationToken cancellationToken)
private SKPicture? GetErrorPicture(int pageNumber, Exception ex, CancellationToken token)
{
if (IsDisposed())
if (token.IsCancellationRequested)
{
return null;
}

// Try get page size
PdfPageInformation info;

try
{
info = _document!.GetPage<PdfPageInformation>(pageNumber);
}
catch (Exception)
{
// TODO
info = new PdfPageInformation()
// Try get page size
PdfPageInformation info;

try
{
Width = 100,
Height = 100,
PageNumber = pageNumber
};
}
info = _document?.GetPage<PdfPageInformation>(pageNumber) ?? throw new NullReferenceException();
}
catch (Exception)
{
if (token.IsCancellationRequested)
{
return null;
}

float width = (float)info.Width;
float height = (float)info.Height;
// TODO
info = new PdfPageInformation()
{
Width = 100,
Height = 100,
PageNumber = pageNumber
};
}

using (var recorder = new SKPictureRecorder())
using (var canvas = recorder.BeginRecording(SKRect.Create(width, height)))
{
float size = 9;
using (var drawTypeface = SKTypeface.CreateDefault())
using (var skFont = drawTypeface.ToFont(size))
using (var paint = new SKPaint())
float width = (float)info.Width;
float height = (float)info.Height;

if (token.IsCancellationRequested)
{
paint.Color = SKColors.Red;
paint.IsAntialias = true;
return null;
}

float lineY = size + 1;
foreach (var textLine in ex.ToString().Split('\n'))
using (var recorder = new SKPictureRecorder())
using (var canvas = recorder.BeginRecording(SKRect.Create(width, height)))
{
float size = 9;
using (var drawTypeface = SKTypeface.CreateDefault())
using (var skFont = drawTypeface.ToFont(size))
using (var paint = new SKPaint())
{
canvas.DrawShapedText(textLine, new SKPoint(0, lineY), skFont, paint);
lineY += size;
paint.Color = SKColors.Red;
paint.IsAntialias = true;

float lineY = size + 1;
foreach (var textLine in ex.ToString().Split('\n'))
{
canvas.DrawShapedText(textLine, new SKPoint(0, lineY), skFont, paint);
lineY += size;
}
}
}

canvas.Flush();
canvas.Flush();

return recorder.EndRecording();
return recorder.EndRecording();
}
}
catch (Exception e)
{
Debug.WriteExceptionToFile(e);
}

return null;
}
}
}
8 changes: 4 additions & 4 deletions Caly.Core/Services/PdfPigPdfService.Thumbnail.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ private async Task SetThumbnail(PageViewModel vm, SKPicture picture, Cancellatio

SKMatrix scale = SKMatrix.CreateScale(tWidth / (float)vm.Width * (float)PpiScale,
tHeight / (float)vm.Height * (float)PpiScale);


token.ThrowIfCancellationRequested();

using (var surface = SKSurface.Create(new SKImageInfo(tWidth, tHeight)))
using (var canvas = surface.Canvas)
{
token.ThrowIfCancellationRequested();

canvas.Clear(SKColors.White);
canvas.DrawPicture(picture, in scale);

Expand All @@ -81,7 +81,7 @@ private async Task SetThumbnail(PageViewModel vm, SKPicture picture, Cancellatio
{
var thumbnail = Bitmap.DecodeToWidth(stream, vm.ThumbnailWidth, BitmapInterpolationMode.LowQuality);

Dispatcher.UIThread.Invoke(() => vm.Thumbnail = thumbnail);
Dispatcher.UIThread.Invoke(() => vm.Thumbnail = thumbnail, DispatcherPriority.Send, token);

if (!_bitmaps.TryAdd(vm.PageNumber, vm))
{
Expand Down
Loading