Convert PowerPoint Presentations to Markdown in C++

Overview

Aspose.Slides for C++ can convert PPT and PPTX presentations to Markdown for documentation, static-site, content-migration, and version-control workflows. You can choose a Markdown flavor, control how slide content is rendered, and decide where exported images are stored and how the generated Markdown references them.

By default, Markdown export uses text-only output. To export visual content, set the MarkdownSaveOptions::set_ExportType method to the Sequential or Visual value from the MarkdownExportType enumeration. Sequential renders slide items separately and in order, whereas Visual keeps grouped items together to preserve their visual relationship. The TextOnly value does not emit image resources, so the image-saving events are not invoked in that mode.

Convert a Presentation to Markdown

Load the source file with the Presentation class, and then call the Presentation::Save method with the Md value from the SaveFormat enumeration.

#include <DOM/Presentation.h>
#include <Export/SaveFormat.h>

using namespace Aspose::Slides;
using namespace Aspose::Slides::Export;

auto presentation = System::MakeObject<Presentation>(u"presentation.pptx");
presentation->Save(u"presentation.md", SaveFormat::Md);

Select a Markdown Flavor

The MarkdownSaveOptions::set_Flavor method controls the Markdown specification used for the output. The Flavor enumeration includes CommonMark, GitHub Flavored Markdown, and other supported variants.

The following example exports a presentation as CommonMark:

#include <DOM/Presentation.h>
#include <Export/Markdown/SaveOptions/Flavor.h>
#include <Export/Markdown/SaveOptions/MarkdownSaveOptions.h>
#include <Export/SaveFormat.h>

using namespace Aspose::Slides;
using namespace Aspose::Slides::Export;

auto presentation = System::MakeObject<Presentation>(u"presentation.pptx");
auto options = System::MakeObject<MarkdownSaveOptions>();
options->set_Flavor(Flavor::CommonMark);

presentation->Save(u"presentation.md", SaveFormat::Md, options);

Export Images Using the Default Local-Saving Behavior

The MarkdownSaveOptions class provides two methods for configuring locally saved images:

The following example renders visual content, writes images to output/assets, and creates relative image references in the Markdown document:

#include <DOM/Presentation.h>
#include <Export/Markdown/SaveOptions/MarkdownExportType.h>
#include <Export/Markdown/SaveOptions/MarkdownSaveOptions.h>
#include <Export/SaveFormat.h>
#include <system/io/directory.h>
#include <system/io/path.h>

using namespace Aspose::Slides;
using namespace Aspose::Slides::Export;
using namespace System::IO;

const System::String outputDirectory = u"output";
Directory::CreateDirectory_(outputDirectory);

auto presentation = System::MakeObject<Presentation>(u"presentation.pptx");
auto options = System::MakeObject<MarkdownSaveOptions>();
options->set_ExportType(MarkdownExportType::Visual);
options->set_BasePath(outputDirectory);
options->set_ImagesSaveFolderName(u"assets");

auto markdownPath = Path::Combine(outputDirectory, u"presentation.md");
presentation->Save(markdownPath, SaveFormat::Md, options);

This behavior also serves as the fallback when a custom image-saving handler returns false.

Use the MarkdownSaveOptions::ImageSaving event for non-SVG bitmap and metafile resources emitted during Markdown export. Its MarkdownImageSavingHandler delegate receives the IImage object, its ImageFormat, and the generated Markdown link as a System::String& parameter. Save or upload the image with the supplied format, and replace link with the reference that must appear in the Markdown output.

Resources emitted in SVG format are handled separately. Subscribe to the MarkdownSaveOptions::SvgImageSaving event, whose MarkdownSvgImageSavingHandler delegate receives an ISvgImage object and the System::String& link parameter. An SVG has no ImageFormat argument; write or upload its XML data from the ISvgImage::get_SvgData method instead. Depending on the export mode and visual grouping, an SVG in the source presentation can be rasterized or combined with other content; the resulting non-SVG resource is then passed to ImageSaving. Subscribe to both events when every exported visual resource requires custom processing.

The handler return value determines who processes the image:

  • Return true after the handler has saved, uploaded, transformed, or otherwise processed the image and assigned a valid value to link. Aspose.Slides writes that value to the Markdown document and does not perform its default local save.
  • Return false to let Aspose.Slides save the image locally and generate its link according to MarkdownSaveOptions::set_BasePath and MarkdownSaveOptions::set_ImagesSaveFolderName.

Save Images to a CDN Origin Directory and Use External URLs

The following example treats cdn-origin/presentations/quarterly-report as a mounted or synchronized CDN origin directory. Each handler extracts the generated file name, saves the image to that custom directory, and replaces the generated local reference with a public CDN URL. The sample itself performs no network upload: the URL becomes valid only after the directory is mounted as the CDN origin or its files are published to the CDN. For object storage, replace the file-system write with the storage SDK’s upload operation and assign link only after the upload succeeds.

#include <DOM/ISvgImage.h>
#include <DOM/Presentation.h>
#include <Export/Markdown/SaveOptions/MarkdownExportType.h>
#include <Export/Markdown/SaveOptions/MarkdownSaveOptions.h>
#include <Export/SaveFormat.h>
#include <IImage.h>
#include <ImageFormat.h>
#include <functional>
#include <system/io/directory.h>
#include <system/io/file.h>
#include <system/io/path.h>
#include <system/uri.h>

using namespace Aspose::Slides;
using namespace Aspose::Slides::Export;
using namespace System::IO;

const System::String outputDirectory = u"output";
const System::String publicBaseUrl = u"https://cdn.example.com/presentations/quarterly-report";
const System::String storageDirectory = Path::Combine(u"cdn-origin", u"presentations", u"quarterly-report");
Directory::CreateDirectory_(outputDirectory);
Directory::CreateDirectory_(storageDirectory);

auto presentation = System::MakeObject<Presentation>(u"presentation.pptx");
auto options = System::MakeObject<MarkdownSaveOptions>();
options->set_ExportType(MarkdownExportType::Visual);
options->set_BasePath(outputDirectory);
options->set_ImagesSaveFolderName(u"fallback-images");

options->ImageSaving.connect(std::function<bool(System::SharedPtr<IImage>, ImageFormat, System::String&)>([storageDirectory, publicBaseUrl](System::SharedPtr<IImage> image, ImageFormat format, System::String& link) -> bool
{
    if (image->get_Width() < 128 || image->get_Height() < 128)
    {
        return false;
    }

    auto urlCompatibleLink = link.Replace(u"\\", u"/");
    auto fileName = urlCompatibleLink.Substring(urlCompatibleLink.LastIndexOf(u'/') + 1);
    auto storagePath = Path::Combine(storageDirectory, fileName);
    image->Save(storagePath, format);
    link = publicBaseUrl + u"/" + System::Uri::EscapeDataString(fileName);
    return true;
}));

options->SvgImageSaving.connect(std::function<bool(System::SharedPtr<ISvgImage>, System::String&)>([storageDirectory, publicBaseUrl](System::SharedPtr<ISvgImage> svgImage, System::String& link) -> bool
{
    auto urlCompatibleLink = link.Replace(u"\\", u"/");
    auto fileName = urlCompatibleLink.Substring(urlCompatibleLink.LastIndexOf(u'/') + 1);
    auto storagePath = Path::Combine(storageDirectory, fileName);
    File::WriteAllBytes(storagePath, svgImage->get_SvgData());
    link = publicBaseUrl + u"/" + System::Uri::EscapeDataString(fileName);
    return true;
}));

auto markdownPath = Path::Combine(outputDirectory, u"presentation.md");
presentation->Save(markdownPath, SaveFormat::Md, options);

The bitmap handler deliberately returns false for images smaller than 128 × 128 pixels, so Aspose.Slides saves those images to output/fallback-images using the default behavior. Larger bitmap and metafile resources, as well as SVG resources, are handled by the custom code. For example, a generated local reference such as fallback-images/image1.png becomes https://cdn.example.com/presentations/quarterly-report/image1.png. The handlers use operating-system paths only when writing files; links written to Markdown use forward slashes and URL-escaped file names. Apply the same rule when building relative links: use /, not the platform-specific directory separator.

FAQ

Can one handler process both raster images and SVG images?

No. Use MarkdownSaveOptions::ImageSaving for emitted bitmap and metafile resources and MarkdownSaveOptions::SvgImageSaving for resources emitted as SVG. The former provides an IImage object and an ImageFormat; the latter provides an ISvgImage object whose SVG data can be read with ISvgImage::get_SvgData. A source SVG that is rasterized during export is processed by ImageSaving instead.

What happens when an image-saving handler returns false?

Aspose.Slides uses its default local-saving behavior. The image location and generated reference are controlled by MarkdownSaveOptions::set_BasePath and MarkdownSaveOptions::set_ImagesSaveFolderName.

Can a handler provide a URL without saving the image locally?

Yes. The handler can upload the image to object storage or pass it to another service, assign the resulting URL to link, and return true. The handler must complete the processing itself; returning true prevents the default local save.

Why does Markdown export throw an InvalidOperationException from a handler?

This exception occurs when the handler returns true but does not provide a valid link. Assign the relative path or external URL that should be written to Markdown before returning true.

Which path separator should image links use?

Use forward slashes in Markdown links and URLs. Use Path::Combine only for file-system paths, then construct or normalize the Markdown reference separately.

Are hyperlinks preserved during Markdown export?

Yes. Text hyperlinks are preserved as standard Markdown links. Slide transitions and animations are not converted.

Can presentations be converted to Markdown in parallel?

You can process different presentation files in parallel, but do not share the same Presentation instance between threads. Follow the multithreading guidelines and use a separate instance for each file.