Skip to content

Commit c877a30

Browse files
authored
feat(Html2Image): add IHtml2Image package (#333)
* feat: 增加 BootstrapBlazor.Html2Image 包 * feat: 增加 MethodName 参数 * refactor: 更新内部逻辑 * feat: 增加内部逻辑代码 * chore: 增加 Html2Image 工程 * refactor: 移动位置 * refactor: 代码格式化 * chore: bump version 9.0.0-beta01
1 parent 0b9ebb9 commit c877a30

7 files changed

Lines changed: 279 additions & 0 deletions

File tree

BootstrapBlazor.Extensions.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.IP2Region",
174174
EndProject
175175
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.JuHeIpLocatorProvider", "src\components\BootstrapBlazor.JuHeIpLocatorProvider\BootstrapBlazor.JuHeIpLocatorProvider.csproj", "{C882891F-4A6A-C9BE-AC96-227764A4A46A}"
176176
EndProject
177+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Html2Image", "src\components\BootstrapBlazor.Html2Image\BootstrapBlazor.Html2Image.csproj", "{F184D96E-7855-4E3B-B447-D09DBC1C91C6}"
178+
EndProject
177179
Global
178180
GlobalSection(SolutionConfigurationPlatforms) = preSolution
179181
Debug|Any CPU = Debug|Any CPU
@@ -468,6 +470,10 @@ Global
468470
{C882891F-4A6A-C9BE-AC96-227764A4A46A}.Debug|Any CPU.Build.0 = Debug|Any CPU
469471
{C882891F-4A6A-C9BE-AC96-227764A4A46A}.Release|Any CPU.ActiveCfg = Release|Any CPU
470472
{C882891F-4A6A-C9BE-AC96-227764A4A46A}.Release|Any CPU.Build.0 = Release|Any CPU
473+
{F184D96E-7855-4E3B-B447-D09DBC1C91C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
474+
{F184D96E-7855-4E3B-B447-D09DBC1C91C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
475+
{F184D96E-7855-4E3B-B447-D09DBC1C91C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
476+
{F184D96E-7855-4E3B-B447-D09DBC1C91C6}.Release|Any CPU.Build.0 = Release|Any CPU
471477
EndGlobalSection
472478
GlobalSection(SolutionProperties) = preSolution
473479
HideSolutionNode = FALSE
@@ -549,6 +555,7 @@ Global
549555
{AA4EDA37-1D81-4235-A7F6-F1C112B364EF} = {FF1089BE-C704-4374-B629-C57C08E1798F}
550556
{8A46FEDF-FD76-48AB-9BB2-47254C7623A4} = {FF1089BE-C704-4374-B629-C57C08E1798F}
551557
{C882891F-4A6A-C9BE-AC96-227764A4A46A} = {FF1089BE-C704-4374-B629-C57C08E1798F}
558+
{F184D96E-7855-4E3B-B447-D09DBC1C91C6} = {FF1089BE-C704-4374-B629-C57C08E1798F}
552559
EndGlobalSection
553560
GlobalSection(ExtensibilityGlobals) = postSolution
554561
SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Razor">
2+
3+
<PropertyGroup>
4+
<Version>9.0.0-beta01</Version>
5+
</PropertyGroup>
6+
7+
<PropertyGroup>
8+
<PackageTags>Bootstrap Blazor WebAssembly wasm UI Components HtmlToImage</PackageTags>
9+
<Description>Bootstrap UI components extensions of Html2Pdf use html-to-image lib</Description>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="BootstrapBlazor" Version="9.3.1-beta32" />
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Website: https://www.blazor.zone or https://argozhang.github.io/
4+
5+
using BootstrapBlazor.Components;
6+
7+
namespace Microsoft.Extensions.DependencyInjection;
8+
9+
/// <summary>
10+
/// BootstrapBlazor 服务扩展类
11+
/// </summary>
12+
public static class BootstrapBlazorHtml2PdfServiceExtensions
13+
{
14+
/// <summary>
15+
/// 添加 AzureOpenAIService 服务
16+
/// </summary>
17+
/// <param name="services"></param>
18+
public static IServiceCollection AddBootstrapBlazorHtml2ImageService(this IServiceCollection services)
19+
{
20+
services.AddScoped<IHtml2Image, DefaultHtml2ImageService>();
21+
#if NET8_0_OR_GREATER
22+
services.AddKeyedScoped<IHtml2Image, DefaultHtml2ImageService>("BootstrapBlazor.Html2Image");
23+
#endif
24+
return services;
25+
}
26+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
5+
6+
using Microsoft.Extensions.Logging;
7+
using Microsoft.JSInterop;
8+
9+
namespace BootstrapBlazor.Components;
10+
11+
/// <summary>
12+
/// 默认 Html to Image 实现
13+
/// <param name="runtime"></param>
14+
/// <param name="logger"></param>
15+
/// </summary>
16+
class DefaultHtml2ImageService(IJSRuntime runtime, ILogger<DefaultHtml2ImageService> logger) : IHtml2Image
17+
{
18+
private JSModule? _jsModule;
19+
20+
/// <summary>
21+
/// <inheritdoc/>
22+
/// </summary>
23+
public Task<string?> GetDataAsync(string selector, IHtml2ImageOptions? options) => Execute(selector, options);
24+
25+
/// <summary>
26+
/// <inheritdoc/>
27+
/// </summary>
28+
public Task<Stream?> GetStreamAsync(string selector, IHtml2ImageOptions? options) => ToBlob(selector, options);
29+
30+
private async Task<string?> Execute(string selector, IHtml2ImageOptions? options)
31+
{
32+
string? data = null;
33+
try
34+
{
35+
_jsModule ??= await LoadModule();
36+
if (_jsModule != null)
37+
{
38+
data = await _jsModule.InvokeAsync<string?>("execute", selector, "getData", options);
39+
}
40+
}
41+
catch (Exception ex)
42+
{
43+
logger.LogError(ex, "{Execute} throw exception", nameof(Execute));
44+
}
45+
return data;
46+
}
47+
48+
private async Task<Stream?> ToBlob(string selector, IHtml2ImageOptions? options)
49+
{
50+
Stream? data = null;
51+
try
52+
{
53+
_jsModule ??= await LoadModule();
54+
if (_jsModule != null)
55+
{
56+
var streamRef = await _jsModule.InvokeAsync<IJSStreamReference>("execute", selector, "toBlob", options);
57+
if (streamRef != null)
58+
{
59+
data = await streamRef.OpenReadStreamAsync(streamRef.Length);
60+
}
61+
}
62+
}
63+
catch (Exception ex)
64+
{
65+
logger.LogError(ex, "{ToBlob} throw exception", nameof(ToBlob));
66+
}
67+
return data;
68+
}
69+
70+
private Task<JSModule> LoadModule() => runtime.LoadModule("./_content/BootstrapBlazor.Html2Image/html2image.js");
71+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
5+
6+
using System.Text.Json.Serialization;
7+
8+
namespace BootstrapBlazor.Components;
9+
10+
/// <summary>
11+
/// Html2Image 选项类
12+
/// </summary>
13+
public class Html2ImageOptions : IHtml2ImageOptions
14+
{
15+
/// <summary>
16+
/// Width in pixels to be applied to node before rendering.
17+
/// </summary>
18+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
19+
public int? Width { get; set; }
20+
21+
/// <summary>
22+
/// Height in pixels to be applied to node before rendering.
23+
/// </summary>
24+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
25+
public int? Height { get; set; }
26+
27+
/// <summary>
28+
/// A string value for the background color, any valid CSS color value.
29+
/// </summary>
30+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
31+
public string? BackgroundColor { get; set; }
32+
33+
/// <summary>
34+
/// Width in pixels to be applied to canvas on export.
35+
/// </summary>
36+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
37+
public int? CanvasWidth { get; set; }
38+
39+
/// <summary>
40+
/// Height in pixels to be applied to canvas on export.
41+
/// </summary>
42+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
43+
public int? CanvasHeight { get; set; }
44+
45+
/// <summary>
46+
/// An array of style properties to be copied to node's style before rendering.
47+
/// For performance-critical scenarios, users may want to specify only the required properties instead of all styles.
48+
/// </summary>
49+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
50+
public string[]? IncludeStyleProperties { get; set; }
51+
52+
/// <summary>
53+
/// A number between `0` and `1` indicating image quality (e.g. 0.92 => 92%)
54+
/// of the JPEG image.
55+
/// </summary>
56+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
57+
public double? Quality { get; set; }
58+
59+
/// <summary>
60+
/// The pixel ratio of captured image. Default is the actual pixel ratio of
61+
/// the device. Set 1 to use as initial-scale 1 for the image
62+
/// </summary>
63+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
64+
public double? PixelRatio { get; set; }
65+
66+
/// <summary>
67+
/// A string indicating the image format. The default type is image/png; that type is also used if the given type isn't supported.
68+
/// </summary>
69+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
70+
public string? Type { get; set; }
71+
72+
/// <summary>
73+
/// An object whose properties to be copied to node's style before rendering.
74+
/// </summary>
75+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
76+
public Dictionary<string, string>? Style { get; set; }
77+
78+
/// <summary>
79+
/// Set to `true` to append the current time as a query string to URL
80+
/// requests to enable cache busting.
81+
/// </summary>
82+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
83+
public bool? CacheBust { get; set; }
84+
85+
/// <summary>
86+
/// Set false to use all URL as cache key.
87+
/// Default: false | undefined - which strips away the query parameters
88+
/// </summary>
89+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
90+
public bool? IncludeQueryParams { get; set; }
91+
92+
/// <summary>
93+
/// A data URL for a placeholder image that will be used when fetching
94+
/// an image fails. Defaults to an empty string and will render empty
95+
/// areas for failed images.
96+
/// </summary>
97+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
98+
public string? ImagePlaceholder { get; set; }
99+
100+
/// <summary>
101+
/// Option to skip the fonts download and embed.
102+
/// </summary>
103+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
104+
public bool? SkipFonts { get; set; }
105+
106+
/// <summary>
107+
/// The preferred font format. If specified all other font formats are ignored.
108+
/// </summary>
109+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
110+
public string? PreferredFontFormat { get; set; }
111+
112+
/// <summary>
113+
/// A CSS string to specify for font embeds. If specified only this CSS will
114+
/// be present in the resulting image. Use with `getFontEmbedCSS()` to
115+
/// create embed CSS for use across multiple calls to library functions.
116+
/// </summary>
117+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
118+
public string? FontEmbedCSS { get; set; }
119+
120+
/// <summary>
121+
/// A boolean to turn off auto scaling for truly massive images.
122+
/// </summary>
123+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
124+
public bool? SkipAutoScale { get; set; }
125+
126+
/// <summary>
127+
/// 执行方法名称 ToPng ToJpeg ToSvg ToCanvas
128+
/// </summary>
129+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
130+
public string? MethodName { get; set; }
131+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import './lib/html-to-image.js'
2+
3+
export async function execute(selector, methodName, options) {
4+
let data = null;
5+
const el = document.querySelector(selector);
6+
if (el) {
7+
const fn = methodName === 'toBlob'
8+
? htmlToImage[methodName]
9+
: getMethod(options);
10+
data = await fn(el, options);
11+
}
12+
return data;
13+
}
14+
15+
const getMethod = options => {
16+
let ret = "toPng";
17+
if (options) {
18+
const { methodName } = options;
19+
delete options.methodName;
20+
21+
if (['toPng', 'toJpeg', 'toSvg', 'toCanvas'].find(i => i === methodName)) {
22+
ret = methodName;
23+
}
24+
}
25+
return htmlToImage[ret];
26+
}

src/components/BootstrapBlazor.Html2Image/wwwroot/lib/html-to-image.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)