在 .NET 中将 PowerPoint 演示文稿转换为 HTML

概述

Aspose.Slides for .NET 可以在没有 Microsoft PowerPoint 的情况下将 PowerPoint 演示文稿保存为 HTML。基本的转换只需一次 Presentation 加载并使用 Save 调用配合 SaveFormat。当需要控制导出的布局、字体、图像、备注、评论、SVG 输出或链接资源时,请使用 HtmlOptions

本指南侧重于实用的 HTML 导出场景:

  • 导出整个演示文稿或选定的幻灯片。
  • 生成固定布局、响应式或基于 SVG 的 HTML。
  • 包含演讲者备注和评论。
  • 控制图像质量和裁剪图像数据。
  • 嵌入字体或单独保存字体文件。
  • 选择如何写入和引用外部资源和媒体文件。

默认情况下,HTML 导出会生成一个自包含的 HTML 文档,大多数资源都已嵌入。这对于共享单个文件很方便,但可能会增加输出大小。对于 Web 发布,请考虑使用外部资源、降低图像 DPI,并仅嵌入在目标环境中不可靠的字体。

将演示文稿转换为 HTML

要将演示文稿导出为 HTML,请使用 Presentation 加载它,并使用 SaveFormat.Html 保存。

using var presentation = new Presentation("presentation.pptx");

presentation.Save("presentation.html", SaveFormat.Html);

此示例会写入一个 HTML 文件。演示文稿对象在 using 声明中被释放,导出后会释放文件句柄和渲染资源。

使用 HtmlOptions

HtmlOptions 是 HTML 导出的主要配置类。常见设置包括:

  • SlidesLayoutOptions:添加备注、评论、讲义或其他布局信息。
  • HtmlFormatter:更改 HTML 文档结构或将格式化委托给控制器。
  • SlideImageFormat:更改幻灯片的表示方式,例如 SVG。
  • PicturesCompression:控制图像 DPI 和输出大小。
  • DeletePicturesCroppedAreas:保留或移除裁剪的图像数据。
  • SvgResponsiveLayout:使导出的 SVG 内容适应其容器。
  • ShowHiddenSlides:在需要时包含隐藏的幻灯片。

以下章节分别展示最常用的选项,您可以仅组合工作流所需的部分。

将选定的幻灯片转换为 HTML

接受幻灯片编号的 Presentation.Save 重载使用基于 1 的幻灯片位置。下面的循环将每张幻灯片保存为单独的 HTML 文件。

using var presentation = new Presentation("presentation.pptx");

var slideCount = presentation.Slides.Count;

for (var slideIndex = 0; slideIndex < slideCount; slideIndex++)
{
    var slideNumber = slideIndex + 1;
    var slideNumbers = new[] { slideNumber };
    var htmlFileName = $"slide-{slideNumber}.html";

    presentation.Save(htmlFileName, slideNumbers, SaveFormat.Html);
}

当网站或应用需要每张幻灯片对应一个 HTML 页面时使用此模式。如果每张幻灯片应使用相同的布局,请创建一个 HtmlOptions 实例并将其传递给每个 Save 调用。

创建响应式 HTML

ResponsiveHtmlController 通过 HtmlFormatter 提供响应式 HTML 输出。当导出页面需要更好地适应浏览器宽度时使用它。

using var presentation = new Presentation("presentation.pptx");

var controller = new ResponsiveHtmlController();
var formatter = HtmlFormatter.CreateCustomFormatter(controller);

var htmlOptions = new HtmlOptions
{
    HtmlFormatter = formatter
};

presentation.Save("presentation-responsive.html", SaveFormat.Html, htmlOptions);

对于基于 SVG 的响应式布局,请在 HtmlOptions 上设置 SvgResponsiveLayout。当幻灯片内容以可伸缩的 SVG 标记导出时,这非常有用。

using var presentation = new Presentation("presentation.pptx");

var htmlOptions = new HtmlOptions
{
    SvgResponsiveLayout = true
};

presentation.Save("presentation-svg-responsive.html", SaveFormat.Html, htmlOptions);

包含演讲者备注和评论

通过 HtmlOptions.SlidesLayoutOptions 使用 NotesCommentsLayoutingOptions 可以包含演讲者备注或评论。除非指定其位置,否则备注和评论默认是隐藏的。

假设源演示文稿包含演讲者备注:

PowerPoint 中带有演讲者备注的幻灯片

下面的代码会在幻灯片下方导出演讲者备注。

using var presentation = new Presentation("presentation.pptx");

var layoutOptions = new NotesCommentsLayoutingOptions
{
    NotesPosition = NotesPositions.BottomFull
};

var htmlOptions = new HtmlOptions
{
    SlidesLayoutOptions = layoutOptions
};

presentation.Save("presentation-with-notes.html", SaveFormat.Html, htmlOptions);

导出的 HTML 包含备注区域:

带有幻灯片和演讲者备注的 HTML 输出

要导出评论,请设置 CommentsPosition,例如 CommentsPositions.RightCommentsPositions.Bottom。如果只需要评论,请省略 NotesPosition。如果需要同时包含备注和评论,请同时设置这两个属性。

控制图像质量和裁剪区域

HTML 导出可以压缩幻灯片图像以减小输出大小。当需要更高图像质量时,请将 PicturesCompression 设置为 PicturesCompression 中的某个值。

using var presentation = new Presentation("presentation.pptx");

var htmlOptions = new HtmlOptions
{
    PicturesCompression = PicturesCompression.Dpi150
};

presentation.Save("presentation-dpi-150.html", SaveFormat.Html, htmlOptions);

默认情况下,图像的裁剪区域可能会从导出结果中移除。仅在用户必须能够恢复或检查这些隐藏图像部分时才保留裁剪数据。保留它会增加 HTML 大小。

using var presentation = new Presentation("presentation.pptx");

var htmlOptions = new HtmlOptions
{
    DeletePicturesCroppedAreas = false
};

presentation.Save("presentation-with-cropped-areas.html", SaveFormat.Html, htmlOptions);

添加 CSS

对于简易样式,可将 CSS 字符串传递给 HtmlFormatter.CreateDocumentFormatter。这会修改外围 HTML 文档,而 Aspose.Slides 继续渲染幻灯片内容。

using var presentation = new Presentation("presentation.pptx");

var cssRules = "body { margin: 0; background: #f7f7f7; } .slide { margin: 24px auto; }";
var formatter = HtmlFormatter.CreateDocumentFormatter(cssRules, true);

var htmlOptions = new HtmlOptions
{
    HtmlFormatter = formatter
};

presentation.Save("presentation-styled.html", SaveFormat.Html, htmlOptions);

若需自定义文档头部、链接的 CSS 文件或在幻灯片和形状周围添加自定义标记,请实现 IHtmlFormattingController 并使用 CreateCustomFormatter 将其传递给 HtmlFormatter

嵌入字体

如果目标环境可能没有安装演示文稿使用的字体,请使用 EmbedAllFontsHtmlController 将字体嵌入 HTML。嵌入可提升视觉保真度,但会增加输出大小。

using var presentation = new Presentation("presentation.pptx");

string[] fontNamesToExclude = { "Arial", "Calibri" };
var fontController = new EmbedAllFontsHtmlController(fontNamesToExclude);
var formatter = HtmlFormatter.CreateCustomFormatter(fontController);

var htmlOptions = new HtmlOptions
{
    HtmlFormatter = formatter
};

presentation.Save("presentation-embedded-fonts.html", SaveFormat.Html, htmlOptions);

仅当您确信目标浏览器或系统已经提供这些字体时才排除它们。对于品牌字体或不常见的字体,嵌入通常更安全。

链接字体文件而不是嵌入

为降低 HTML 文件大小,您可以将字体数据写入单独的 WOFF 文件,并在 HTML 中添加 @font-face 规则。下面的帮助类扩展了 EmbedAllFontsHtmlController 并重写了 WriteFont

using var presentation = new Presentation("presentation.pptx");

var outputDirectory = Path.Combine(Environment.CurrentDirectory, "html-output");
var fontsDirectory = Path.Combine(outputDirectory, "fonts");
Directory.CreateDirectory(outputDirectory);

var fontController = new LinkedFontsHtmlController(fontsDirectory, "fonts");
var formatter = HtmlFormatter.CreateCustomFormatter(fontController);

var htmlOptions = new HtmlOptions
{
    HtmlFormatter = formatter
};

var htmlFilePath = Path.Combine(outputDirectory, "presentation.html");
presentation.Save(htmlFilePath, SaveFormat.Html, htmlOptions);
public sealed class LinkedFontsHtmlController : EmbedAllFontsHtmlController
{
    private readonly string _fontOutputDirectory;
    private readonly string _fontUrlPrefix;

    public LinkedFontsHtmlController(
        string fontOutputDirectory,
        string fontUrlPrefix)
        : base(Array.Empty<string>())
    {
        _fontOutputDirectory = fontOutputDirectory;
        _fontUrlPrefix = fontUrlPrefix.TrimEnd('/') + "/";

        Directory.CreateDirectory(_fontOutputDirectory);
    }

    public override void WriteFont(
        IHtmlGenerator generator,
        IFontData originalFont,
        IFontData substitutedFont,
        string fontStyle,
        string fontWeight,
        byte[] fontData)
    {
        var font = substitutedFont ?? originalFont;
        var safeFontName = MakeSafeFileName(font.FontName);
        var safeFontStyle = string.IsNullOrWhiteSpace(fontStyle) ? "normal" : fontStyle;
        var safeFontWeight = string.IsNullOrWhiteSpace(fontWeight) ? "normal" : fontWeight;
        var fontFileName = $"{safeFontName}-{safeFontStyle}-{safeFontWeight}.woff";
        var fontFilePath = Path.Combine(_fontOutputDirectory, fontFileName);

        File.WriteAllBytes(fontFilePath, fontData);

        var fontUrl = _fontUrlPrefix + Uri.EscapeDataString(fontFileName);
        var fontFamily = font.FontName.Replace("\\", "\\\\").Replace("'", "\\'");

        generator.AddHtml("<style>");
        generator.AddHtml("@font-face {");
        generator.AddHtml($"font-family: '{fontFamily}';");
        generator.AddHtml($"font-style: {safeFontStyle};");
        generator.AddHtml($"font-weight: {safeFontWeight};");
        generator.AddHtml($"src: url('{fontUrl}') format('woff');");
        generator.AddHtml("}");
        generator.AddHtml("</style>");
    }

    private static string MakeSafeFileName(string fileName)
    {
        var invalidCharacters = Path.GetInvalidFileNameChars();
        var safeCharacters = fileName.ToCharArray();

        for (var characterIndex = 0; characterIndex < safeCharacters.Length; characterIndex++)
        {
            if (Array.IndexOf(invalidCharacters, safeCharacters[characterIndex]) >= 0)
            {
                safeCharacters[characterIndex] = '_';
            }
        }

        return new string(safeCharacters);
    }
}

在此示例中,字体文件保存到 html-output/fonts,HTML 使用如 fonts/BrandFont-normal-400.woff 的 URL 引用它们。如果 HTML 文件和字体部署到其他位置,请使用 fontUrlPrefix 使其匹配发布后的 URL 路径。

外部保存资源

自包含的 HTML 易于搬迁,但嵌入的 Base64 资源会使文件变大。如果您的应用需要外部图像文件,请实现 ILinkEmbedController 并将其传递给 HtmlOptions 构造函数。

外部化资源时,请有意识地选择两条路径:

  • 文件系统输出路径:您的应用写入生成的图像、字体、音频或视频的目录。
  • URL 路径:浏览器从 HTML 文档加载这些文件时使用的路径。

完整的图像链接实现请参见 Export Presentations to HTML with Externally Linked Images

导出媒体文件

VideoPlayerHtmlController 导出视频和音频文件,并生成可在浏览器中播放的 HTML。其构造函数接受:

  • path:生成的媒体文件写入的目录。
  • fileName:正在生成的 HTML 文件名。
  • baseUri:HTML 中指向媒体文件的链接所使用的绝对 URI 前缀。

如果 HTML 文件位于 html-output/presentation.html,而媒体文件保存在 html-output/media,则 path 应指向磁盘上的媒体目录,baseUri 则应指向浏览器视角下的同一目录。对于本地预览,可使用 file:/// URI 从媒体目录构造;对于部署的应用,请使用已发布媒体目录的绝对 URL。

var outputDirectory = Path.Combine(Environment.CurrentDirectory, "html-output");
var mediaDirectory = Path.Combine(outputDirectory, "media");
Directory.CreateDirectory(outputDirectory);
Directory.CreateDirectory(mediaDirectory);

var htmlFileName = "presentation.html";
var mediaBaseUri = new Uri(mediaDirectory + Path.DirectorySeparatorChar).AbsoluteUri;

using var presentation = new Presentation();
using var videoStream = new FileStream("intro.mp4", FileMode.Open, FileAccess.Read);

var video = presentation.Videos.AddVideo(videoStream, LoadingStreamBehavior.ReadStreamAndRelease);
var slide = presentation.Slides[0];
slide.Shapes.AddVideoFrame(20, 20, 480, 270, video);

var controller = new VideoPlayerHtmlController(mediaDirectory, htmlFileName, mediaBaseUri);
var formatter = HtmlFormatter.CreateCustomFormatter(controller);
var svgOptions = new SVGOptions(controller);
var slideImageFormat = SlideImageFormat.Svg(svgOptions);

var htmlOptions = new HtmlOptions(controller)
{
    HtmlFormatter = formatter,
    SlideImageFormat = slideImageFormat
};

var htmlFilePath = Path.Combine(outputDirectory, htmlFileName);
presentation.Save(htmlFilePath, SaveFormat.Html, htmlOptions);

在服务器应用中,请为每个导出任务使用唯一的输出目录。共享的输出路径可能导致不同转换的文件相互覆盖。

性能和资源管理

HTML 转换是一次渲染操作,处理时间和内存使用取决于幻灯片数量、图像分辨率、字体、特效、图表和嵌入的媒体。较高的 PicturesCompression DPI 值、嵌入字体、SVG 输出以及保留裁剪图像区域可以提升保真度,但通常会增大输出大小。

批量转换时:

  • 及时释放每个 Presentation 实例。
  • 为不同任务使用独立的输出目录。
  • 除非保真度要求,否则不要嵌入常用字体。
  • 当 HTML 用于预览或缩略图时,降低图像 DPI。
  • 在部署路径确定之前,将源演示文稿、生成的 HTML 与外部资源一起保存。

常见问题

超链接会在 HTML 输出中保留下来吗?

会。演示文稿中的超链接会导出为 HTML,并在目标 URL 有效时保持可点击。

可以并行将演示文稿转换为 HTML 吗?

可以,但请不要在多个线程间共享同一个 Presentation 实例。不同文件应使用独立的演示文稿实例、独立的流和独立的输出目录。详情请参阅 multithreading guidance

Presentation 对象是线程安全的吗?

不是。单个 Presentation 实例应在同一线程上加载、修改、保存并释放。若要并行工作,请为每个线程或进程创建独立实例。

生成的 HTML 文件为什么很大?

默认导出会将资源直接嵌入 HTML。嵌入的字体、高 DPI 图像、媒体、SVG 内容以及保留的裁剪图像区域都会增加大小。使用外部资源、排除常用字体、在对保真度要求不高时降低 PicturesCompression,可以减小文件体积。

在媒体导出时应如何选择 baseUri?

从浏览器的视角选择 baseUri 并以绝对 URI 形式传入。用于本地预览时,可通过 new Uri(mediaDirectory + Path.DirectorySeparatorChar).AbsoluteUri 获得;用于部署时,请使用已发布媒体目录的绝对 URL。文件系统的 path 与浏览器的 baseUri 不必相同,但必须指向同一资源位置。

可以包含隐藏的幻灯片吗?

可以。当必须导出隐藏幻灯片时,将 HtmlOptionsShowHiddenSlides 设置为 true