-
-
Notifications
You must be signed in to change notification settings - Fork 7
feat(Dom2Image): add IDom2ImageService interface #540
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
bd42c1c
af388c5
d8a7a75
80a50e2
380333e
1d50181
a5720c5
02a1cbc
0d5295b
1bd10c6
fe08efd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||
|
|
||
| <PropertyGroup> | ||
| <Version>9.0.0</Version> | ||
| </PropertyGroup> | ||
|
|
||
| <PropertyGroup> | ||
| <PackageTags>Bootstrap Blazor WebAssembly wasm UI Components Dom Image</PackageTags> | ||
| <Description>Bootstrap UI components extensions of DomToImage use snapDom lib</Description> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Content Remove="wwwroot\lib\snapdom.mjs"/> | ||
| <None Include="wwwroot\lib\snapdom.mjs"/> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="BootstrapBlazor" Version="9.0.0"/> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||
| // Copyright (c) Argo Zhang (argo@163.com). All rights reserved. | ||||||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||||||
| // Website: https://www.blazor.zone or https://argozhang.github.io/ | ||||||
|
|
||||||
| using BootstrapBlazor.Components; | ||||||
|
|
||||||
| namespace Microsoft.Extensions.DependencyInjection; | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// BootstrapBlazor 服务扩展类 | ||||||
| /// </summary> | ||||||
| public static class BootstrapBlazorHtml2PdfServiceExtensions | ||||||
| { | ||||||
| /// <summary> | ||||||
| /// 添加 AzureOpenAIService 服务 | ||||||
|
||||||
| /// 添加 AzureOpenAIService 服务 | |
| /// 添加 Dom2Image 服务 |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,85 @@ | ||||||||||||
| // Copyright (c) Argo Zhang (argo@163.com). All rights reserved. | ||||||||||||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||||||||||||
| // Website: https://www.blazor.zone or https://argozhang.github.io/ | ||||||||||||
|
|
||||||||||||
| using Microsoft.Extensions.Logging; | ||||||||||||
| using Microsoft.JSInterop; | ||||||||||||
|
|
||||||||||||
| namespace BootstrapBlazor.Components; | ||||||||||||
|
|
||||||||||||
| /// <summary> | ||||||||||||
| /// 默认 Html to Image 实现 | ||||||||||||
| /// <param name="runtime"></param> | ||||||||||||
| /// <param name="logger"></param> | ||||||||||||
| /// </summary> | ||||||||||||
| class DefaultDom2ImageService(IJSRuntime runtime, ILogger<DefaultDom2ImageService> logger) : IDom2ImageService | ||||||||||||
| { | ||||||||||||
| private JSModule? _jsModule; | ||||||||||||
|
|
||||||||||||
| /// <summary> | ||||||||||||
| /// <inheritdoc/> | ||||||||||||
| /// </summary> | ||||||||||||
| public async Task<string?> GetUrlAsync(string selector, Dom2ImageOptions? options = null, CancellationToken token = default) | ||||||||||||
| { | ||||||||||||
| string? data = null; | ||||||||||||
| try | ||||||||||||
| { | ||||||||||||
| _jsModule ??= await LoadModule(); | ||||||||||||
| data = await _jsModule.InvokeAsync<string?>("getUrl", token, selector, options); | ||||||||||||
| } | ||||||||||||
| catch (OperationCanceledException) { } | ||||||||||||
|
||||||||||||
| catch (Exception ex) | ||||||||||||
| { | ||||||||||||
| logger.LogError(ex, "{GetUrlAsync} throw exception: {ex}", nameof(GetUrlAsync), ex.Format()); | ||||||||||||
| } | ||||||||||||
|
Comment on lines
+31
to
+34
|
||||||||||||
| return data; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| /// <summary> | ||||||||||||
| /// <inheritdoc/> | ||||||||||||
| /// </summary> | ||||||||||||
| public async Task<Stream?> GetStreamAsync(string selector, Dom2ImageOptions? options = null, CancellationToken token = default) | ||||||||||||
| { | ||||||||||||
| Stream? data = null; | ||||||||||||
| try | ||||||||||||
| { | ||||||||||||
| _jsModule ??= await LoadModule(); | ||||||||||||
| var streamReference = await _jsModule.InvokeAsync<IJSStreamReference?>("getStream", selector, options); | ||||||||||||
| if (streamReference != null) | ||||||||||||
| { | ||||||||||||
| data = await streamReference.OpenReadStreamAsync(streamReference.Length, token); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| catch (OperationCanceledException) { } | ||||||||||||
|
||||||||||||
| catch (OperationCanceledException) { } | |
| catch (OperationCanceledException ex) | |
| { | |
| logger.LogWarning(ex, "{GetStreamAsync} operation was canceled.", nameof(GetStreamAsync)); | |
| } |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic catch clause.
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Poor error handling: empty catch block.
| catch (OperationCanceledException) { } | |
| catch (OperationCanceledException ex) | |
| { | |
| logger.LogWarning(ex, "{DownloadAsync} operation was canceled.", nameof(DownloadAsync)); | |
| } |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic catch clause.
| catch (Exception ex) | |
| catch (JSException ex) |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,86 @@ | ||||||||
| // Licensed to the .NET Foundation under one or more agreements. | ||||||||
| // The .NET Foundation licenses this file to you under the Apache 2.0 License | ||||||||
| // See the LICENSE file in the project root for more information. | ||||||||
| // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone | ||||||||
|
|
||||||||
| using System.Text.Json.Serialization; | ||||||||
|
|
||||||||
| namespace BootstrapBlazor.Components; | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Dom2ImageOptions 选项类 | ||||||||
| /// </summary> | ||||||||
| public class Dom2ImageOptions | ||||||||
| { | ||||||||
| /// <summary> | ||||||||
| /// Removes redundant styles. Default value is true | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public bool? Compress { get; set; } | ||||||||
|
ArgoZhang marked this conversation as resolved.
|
||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Skips idle delay for faster results. Default value is true | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public bool? Fast { get; set; } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Inlines fonts (icon fonts always embedded). Default value is false | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public bool? EmbedFonts { get; set; } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Output scale multiplier. Default value is 1 | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public int? Scale { get; set; } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Device pixel ratio | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public int? Dpr { get; set; } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Output specific width size | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public int? Width { get; set; } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Output specific height size | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public int? Height { get; set; } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Fallback color for JPG/WebP. Default value is #fff | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public string? BackgroundColor { get; set; } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Quality for JPG/WebP (0 to 1) | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public float? Quality { get; set; } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Select png, jpg, webp Blob type. Default value is svg | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public string? Type { get; set; } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// CSS selectors for elements to exclude | ||||||||
| /// </summary> | ||||||||
| [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||||||
| public string[]? Exclude { get; set; } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// | ||||||||
|
||||||||
| /// | |
| /// Specifies a list of local font file URLs or font names to embed in the output image. | |
| /// Use this to include custom fonts that are not loaded by default. Provide font file paths or names as strings. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // Copyright (c) Argo Zhang (argo@163.com). All rights reserved. | ||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
| // Website: https://www.blazor.zone or https://argozhang.github.io/ | ||
|
|
||
| namespace BootstrapBlazor.Components; | ||
|
|
||
| /// <summary> | ||
| /// IDom2ImageService 接口定义 | ||
| /// </summary> | ||
| public interface IDom2ImageService | ||
| { | ||
| /// <summary> | ||
| /// 通过指定选择器获得 Html 元素返回图片数据 | ||
| /// </summary> | ||
| /// <param name="selector"></param> | ||
| /// <param name="options"></param> | ||
| /// <param name="token"></param> | ||
| /// <returns></returns> | ||
| Task<string?> GetUrlAsync(string selector, Dom2ImageOptions? options = null, CancellationToken token = default); | ||
|
|
||
| /// <summary> | ||
| /// 通过指定选择器获得 Html 元素返回图片数据流 | ||
| /// </summary> | ||
| /// <param name="selector"></param> | ||
| /// <param name="options"></param> | ||
| /// <param name="token"></param> | ||
| /// <returns></returns> | ||
| Task<Stream?> GetStreamAsync(string selector, Dom2ImageOptions? options = null, CancellationToken token = default); | ||
|
|
||
| /// <summary> | ||
| /// 通过指定选择器下载 Html 元素图片 | ||
| /// </summary> | ||
| /// <param name="selector"></param> | ||
| /// <param name="fileName"></param> | ||
| /// <param name="format"></param> | ||
| /// <param name="backgroundColor"></param> | ||
| /// <param name="options"></param> | ||
| /// <returns></returns> | ||
| Task DownloadAsync(string selector, string fileName = "capture", string? format = "png", string? backgroundColor = null, Dom2ImageOptions? options = null); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,33 @@ | ||||||
| import { snapdom } from './lib/snapdom.min.mjs' | ||||||
|
|
||||||
| export async function getUrl(selector, options = {}) { | ||||||
| let data = null; | ||||||
| const el = document.querySelector(selector); | ||||||
| if (el) { | ||||||
| const result = await snapdom(el, options); | ||||||
| data = result.url; | ||||||
| } | ||||||
| return data; | ||||||
| } | ||||||
|
|
||||||
| export async function getStream(selector, options = {}) { | ||||||
| let data = null; | ||||||
| const el = document.querySelector(selector); | ||||||
| if (el) { | ||||||
| const result = await snapdom(el, options); | ||||||
| data = result.toBlob(); | ||||||
|
||||||
| data = result.toBlob(); | |
| data = await result.toBlob(); |
Uh oh!
There was an error while loading. Please reload this page.