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
22 changes: 22 additions & 0 deletions src/BootstrapBlazor.Server/Components/Samples/Html2Images.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@page "/html2image"

<h3>@Localizer["Html2ImageTitle"]</h3>

<h4>@Localizer["Html2ImageIntro"]</h4>

<DemoBlock Title="@Localizer["Html2ImageElementTitle"]" Introduction="@Localizer["Html2ImageElementIntro"]" Name="Normal">
<Button OnClickWithoutRender="OnExportAsync" Text="@Localizer["Html2ImageButtonText"]" Icon="fa-solid fa-image"></Button>
<Table TItem="Foo" Items="@Items.Take(3)" Id="table-9527">
<TableColumns>
<TableColumn @bind-Field="@context.DateTime" Width="180" />
<TableColumn @bind-Field="@context.Name" Sortable="true" Filterable="true" />
<TableColumn @bind-Field="@context.Address" />
</TableColumns>
</Table>
@if (!string.IsNullOrEmpty(_imageData))
{
<section ignore>
<img src="@_imageData" class="w-100" />
</section>
}
</DemoBlock>
71 changes: 71 additions & 0 deletions src/BootstrapBlazor.Server/Components/Samples/Html2Images.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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([email protected]) Website: https://www.blazor.zone

namespace BootstrapBlazor.Server.Components.Samples;

/// <summary>
/// Html2Image 组件
/// </summary>
public partial class Html2Images
{
/// <summary>
/// 获得 IconTheme 实例
/// </summary>
[Inject]
[NotNull]
private IIconTheme? IconTheme { get; set; }

[Inject]
[NotNull]
private IStringLocalizer<Foo>? LocalizerFoo { get; set; }

[Inject]
[NotNull]
private IHtml2Image? Html2ImageService { get; set; }

[Inject]
[NotNull]
private IStringLocalizer<Html2Images>? Localizer { get; set; }

[Inject]
[NotNull]
private NavigationManager? NavigationManager { get; set; }

[NotNull]
private List<Foo>? Items { get; set; }

private string? _imageData;

/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();

Items = Foo.GenerateFoo(LocalizerFoo);
}

private async Task OnExportAsync()
{
_imageData = await Html2ImageService.GetDataAsync("#table-9527", new Html2ImageOptions()
{
//IncludeStyleProperties = [
// $"{NavigationManager.BaseUri}_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css",
// $"{NavigationManager.BaseUri}_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css",
// $"{NavigationManager.BaseUri}BootstrapBlazor.Server.styles.css",
// $"{NavigationManager.BaseUri}css/site.css"
//]
});
StateHasChanged();

//if (stream != null)
//{
// var reader = new StreamReader(stream);
// var data = await reader.ReadToEndAsync();
// reader.Close();
//}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ void AddQuickStar(DemoMenuItem item)
},
new()
{
IsUpdate = true,
Text = Localizer["Labels"],
Url = "label"
},
Expand Down Expand Up @@ -316,7 +317,6 @@ void AddForm(DemoMenuItem item)
},
new()
{
IsUpdate = true,
Text = Localizer["Cascader"],
Url = "cascader"
},
Expand Down Expand Up @@ -405,7 +405,6 @@ void AddForm(DemoMenuItem item)
},
new()
{
IsUpdate = true,
Text = Localizer["MultiSelect"],
Url = "multi-select"
},
Expand All @@ -431,14 +430,12 @@ void AddForm(DemoMenuItem item)
},
new()
{
IsUpdate = true,
Match = NavLinkMatch.All,
Text = Localizer["Select"],
Url = "select"
},
new()
{
IsUpdate = true,
Text = Localizer["SelectObject"],
Url = "select-object"
},
Expand Down Expand Up @@ -966,7 +963,6 @@ void AddTable(DemoMenuItem item)
},
new()
{
IsUpdate = true,
Text = Localizer["TableLookup"],
Url = "table/lookup"
},
Expand Down Expand Up @@ -1484,6 +1480,12 @@ void AddServices(DemoMenuItem item)
Url = "geolocation"
},
new()
{
IsNew = true,
Text = Localizer["Html2Image"],
Url = "html2image"
},
new()
{
Text = Localizer["Html2Pdf"],
Url = "html2pdf"
Expand Down
8 changes: 8 additions & 0 deletions src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -4771,6 +4771,7 @@
"BaiduOcr": "IBaiduOcr",
"AzureOpenAI": "AzureOpenAI",
"HtmlRenderer": "HtmlRenderer",
"Html2Image": "IHtml2Image",
"Html2Pdf": "IHtml2Pdf",
"Mask": "MaskService",
"ContextMenu": "ContextMenu",
Expand Down Expand Up @@ -6989,5 +6990,12 @@
"NormalIntro": "Set the text to be displayed by setting the <code>Text</code> parameter",
"TypedOptionsTitle": "TypedOptions",
"TypedOptionsIntro": "Customize typing speed, delay, and other settings by setting the properties of the <code>TypedOptions</code> parameter"
},
"BootstrapBlazor.Server.Components.Samples.Html2Images": {
"Html2ImageTitle": "Html to Image",
"Html2ImageIntro": "Convert any area of ​​the web page into an image service",
"Html2ImageElementTitle": "ToPng",
"Html2ImageElementIntro": "Get the <b>base64-encoded</b> image by calling the <code>GetDataAsync</code> method",
"Html2ImageButtonText": "Image"
}
}
8 changes: 8 additions & 0 deletions src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -4771,6 +4771,7 @@
"BaiduOcr": "文字识别服务 IBaiduOcr",
"AzureOpenAI": "AI 聊天服务 AzureOpenAI",
"HtmlRenderer": "Html 转换器 HtmlRenderer",
"Html2Image": "Html 转 Image IHtml2Image",
"Html2Pdf": "Html 转 Pdf IHtml2Pdf",
"Mask": "遮罩服务 MaskService",
"ContextMenu": "右键菜单 ContextMenu",
Expand Down Expand Up @@ -6989,5 +6990,12 @@
"NormalIntro": "通过设置 <code>Text</code> 参数设置要显示的文本",
"TypedOptionsTitle": "TypedOptions",
"TypedOptionsIntro": "通过设置 <code>TypedOptions</code> 参数的属性自定义打字速度、延时等设定"
},
"BootstrapBlazor.Server.Components.Samples.Html2Images": {
"Html2ImageTitle": "Html2Image 网页元素转成图片服务",
"Html2ImageIntro": "将网页中任意区域内容转化成图片服务",
"Html2ImageElementTitle": "ToPng",
"Html2ImageElementIntro": "通过调用 <code>GetDataAsync</code> 方法获得 <b>base64-encoded</b> 图片",
"Html2ImageButtonText": "Image"
}
}
4 changes: 3 additions & 1 deletion src/BootstrapBlazor.Server/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"group-box": "GroupBoxes",
"handwritten": "Handwrittens",
"html-renderer": "HtmlRenderers",
"html2images": "Html2Images",
"html2pdf": "Html2Pdfs",
"label": "Labels",
"layout": "Layouts",
Expand Down Expand Up @@ -294,6 +295,7 @@
"link": {
"AntDesign": "http://www.antblazor.com/",
"Pear Admin": "http://www.pearadmin.com/",
"SAPHP": "https://www.swiftadmin.net/"
"SAPHP": "https://www.swiftadmin.net/",
"Veterinary Hospital": "http://animal.jucun.zone/"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public static IServiceCollection AddBootstrapBlazor(this IServiceCollection serv
// Html2Pdf 服务
services.TryAddSingleton<IHtml2Pdf, DefaultHtml2PdfService>();

// Html2Image 服务
services.TryAddScoped<IHtml2Image, DefaultHtml2ImageService>();

// Table 导出服务
services.TryAddScoped<ITableExport, DefaultTableExport>();

Expand Down
20 changes: 20 additions & 0 deletions src/BootstrapBlazor/Options/Html2ImageOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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([email protected]) Website: https://www.blazor.zone

using System.Text.Json.Serialization;

namespace BootstrapBlazor.Components;

/// <summary>
/// Html2Image 选项类
/// </summary>
public class Html2ImageOptions
{
/// <summary>
/// 获得/设置 样式集合 默认 null
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public List<string>? IncludeStyleProperties { get; set; }

Check warning on line 19 in src/BootstrapBlazor/Options/Html2ImageOptions.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Options/Html2ImageOptions.cs#L19

Added line #L19 was not covered by tests
}
68 changes: 68 additions & 0 deletions src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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([email protected]) Website: https://www.blazor.zone

using Microsoft.Extensions.Logging;

namespace BootstrapBlazor.Components;

/// <summary>
/// 默认 Html to Image 实现
/// <param name="runtime"></param>
/// <param name="logger"></param>
/// </summary>
class DefaultHtml2ImageService(IJSRuntime runtime, ILogger<DefaultHtml2ImageService> logger) : IHtml2Image

Check warning on line 15 in src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs#L15

Added line #L15 was not covered by tests
{
private JSModule? _jsModule;

/// <summary>
/// <inheritdoc/>
/// </summary>
public Task<string?> GetDataAsync(string selector, Html2ImageOptions options) => Execute(selector, "toPng", options);

Check warning on line 22 in src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs#L22

Added line #L22 was not covered by tests

/// <summary>
/// <inheritdoc/>
/// </summary>
public Task<Stream?> GetStreamAsync(string selector, Html2ImageOptions options) => ToBlob(selector, options);

Check warning on line 27 in src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs#L27

Added line #L27 was not covered by tests

private async Task<string?> Execute(string selector, string methodName, Html2ImageOptions options)
{
string? data = null;

Check warning on line 31 in src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs#L30-L31

Added lines #L30 - L31 were not covered by tests
try
{

Check warning on line 33 in src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs#L33

Added line #L33 was not covered by tests
_jsModule ??= await runtime.LoadModuleByName("html2image");
if (_jsModule != null)
{
data = await _jsModule.InvokeAsync<string?>("execute", selector, methodName, options);
}
}
catch (Exception ex)
{
logger.LogError(ex, "{Execute} throw exception", nameof(Execute));
}
return data;
}

Check warning on line 45 in src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs#L36-L45

Added lines #L36 - L45 were not covered by tests

private async Task<Stream?> ToBlob(string selector, Html2ImageOptions options)
{
Stream? data = null;

Check warning on line 49 in src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs#L48-L49

Added lines #L48 - L49 were not covered by tests
try
{

Check warning on line 51 in src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs#L51

Added line #L51 was not covered by tests
_jsModule ??= await runtime.LoadModuleByName("html2image");
if (_jsModule != null)
{
var streamRef = await _jsModule.InvokeAsync<IJSStreamReference>("execute", selector, "toBlob", options);

Check warning on line 55 in src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs#L54-L55

Added lines #L54 - L55 were not covered by tests
if (streamRef != null)
{
data = await streamRef.OpenReadStreamAsync(streamRef.Length);
}
}
}
catch (Exception ex)
{
logger.LogError(ex, "{ToBlob} throw exception", nameof(ToBlob));
}
return data;
}

Check warning on line 67 in src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs

View check run for this annotation

Codecov / codecov/patch

src/BootstrapBlazor/Services/DefaultHtml2ImageService.cs#L57-L67

Added lines #L57 - L67 were not covered by tests
}
26 changes: 26 additions & 0 deletions src/BootstrapBlazor/Services/IHtml2Image.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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([email protected]) Website: https://www.blazor.zone

namespace BootstrapBlazor.Components;

/// <summary>
/// IHtml2Image 接口
/// </summary>
public interface IHtml2Image
{
/// <summary>
/// Export method
/// </summary>
/// <param name="selector">选择器</param>
/// <param name="options"></param>
Task<string?> GetDataAsync(string selector, Html2ImageOptions options);

/// <summary>
/// Export method
/// </summary>
/// <param name="selector">选择器</param>
/// <param name="options"></param>
Task<Stream?> GetStreamAsync(string selector, Html2ImageOptions options);
}
2 changes: 2 additions & 0 deletions src/BootstrapBlazor/wwwroot/lib/html2image/html-to-image.js

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/BootstrapBlazor/wwwroot/modules/html2image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import '../lib/html2image/html-to-image.js'

export async function execute(selector, methodName, options) {
let data = null;
const el = document.querySelector(selector);
if (el) {
const fn = htmlToImage[methodName];
data = await fn(el, {});
}
return data;
}