Convert PowerPoint Presentations to Markdown in JavaScript
Overview
Aspose.Slides for Node.js via Java 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 export type with the MarkdownSaveOptions.setExportType 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 callbacks 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.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
var presentation = new aspose.slides.Presentation("presentation.pptx");
try {
presentation.save("presentation.md", aspose.slides.SaveFormat.Md);
} finally {
presentation.dispose();
}
Select a Markdown Flavor
The MarkdownSaveOptions.setFlavor 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:
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
var presentation = new aspose.slides.Presentation("presentation.pptx");
try {
var options = new aspose.slides.MarkdownSaveOptions();
options.setFlavor(aspose.slides.Flavor.CommonMark);
presentation.save("presentation.md", aspose.slides.SaveFormat.Md, options);
} finally {
presentation.dispose();
}
Export Images Using the Default Local-Saving Behavior
The MarkdownSaveOptions class provides two methods for configuring locally saved images:
- setBasePath specifies the base directory for the Markdown document and its resources.
- setImagesSaveFolderName specifies the image subdirectory. Its default value is
Images.
The following example renders visual content, writes images to output/assets, and creates relative image references in the Markdown document:
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const fs = require("fs");
const path = require("path");
const outputDirectory = "output";
fs.mkdirSync(outputDirectory, { recursive: true });
var presentation = new aspose.slides.Presentation("presentation.pptx");
try {
var options = new aspose.slides.MarkdownSaveOptions();
options.setExportType(aspose.slides.MarkdownExportType.Visual);
options.setBasePath(outputDirectory);
options.setImagesSaveFolderName("assets");
const markdownPath = path.join(outputDirectory, "presentation.md");
presentation.save(markdownPath, aspose.slides.SaveFormat.Md, options);
} finally {
presentation.dispose();
}
This behavior also serves as the fallback when a custom image-saving handler returns false.
Customize Image Saving and Markdown Links
Use the MarkdownSaveOptions.setImageSaving method to register a callback for non-SVG bitmap and metafile resources emitted during Markdown export. Its MarkdownImageSavingHandler callback receives the IImage object, its ImageFormat value, and the generated Markdown link as a one-element string array. Save or upload the image with the supplied format, and replace link[0] with the reference that must appear in the Markdown output.
Resources emitted in SVG format are handled separately. Register a callback with the MarkdownSaveOptions.setSvgImageSaving method. Its MarkdownSvgImageSavingHandler callback receives an ISvgImage object and the one-element link array. An SVG has no ImageFormat argument; write or upload its XML data from the ISvgImage.getSvgData 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 the image-saving callback. Register both callbacks when every exported visual resource requires custom processing.
In Node.js, create implementations of these callback interfaces with java.newProxy.
The handler return value determines who processes the image:
- Return
trueafter the handler has saved, uploaded, transformed, or otherwise processed the image and assigned a valid value tolink[0]. Aspose.Slides writes that value to the Markdown document and does not perform its default local save. - Return
falseto let Aspose.Slides save the image locally and generate its link according to the values set by MarkdownSaveOptions.setBasePath and MarkdownSaveOptions.setImagesSaveFolderName.
Important
A handler that returnstrue takes responsibility for the image. If it returns true without assigning a valid, nonempty link, the export fails with an InvalidOperationException.
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[0] only after the upload succeeds.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");
const fs = require("fs");
const path = require("path");
const outputDirectory = "output";
const publicBaseUrl = "https://cdn.example.com/presentations/quarterly-report";
const storageDirectory = path.join("cdn-origin", "presentations", "quarterly-report");
fs.mkdirSync(outputDirectory, { recursive: true });
fs.mkdirSync(storageDirectory, { recursive: true });
const getFileNameFromLink = generatedLink => {
const urlCompatibleLink = String(generatedLink).replace(/\\/g, "/");
return path.posix.basename(urlCompatibleLink);
};
const buildPublicUrl = fileName => publicBaseUrl + "/" + encodeURIComponent(fileName);
const imageSavingHandler = java.newProxy("com.aspose.slides.MarkdownSaveOptions$MarkdownImageSavingHandler", {
invoke: function(image, format, link) {
if (image.getWidth() < 128 || image.getHeight() < 128) {
return false;
}
const fileName = getFileNameFromLink(link[0]);
const storagePath = path.join(storageDirectory, fileName);
image.save(storagePath, format);
link[0] = buildPublicUrl(fileName);
return true;
}
});
const svgImageSavingHandler = java.newProxy("com.aspose.slides.MarkdownSaveOptions$MarkdownSvgImageSavingHandler", {
invoke: function(svgImage, link) {
const fileName = getFileNameFromLink(link[0]);
const storagePath = path.join(storageDirectory, fileName);
fs.writeFileSync(storagePath, svgImage.getSvgData());
link[0] = buildPublicUrl(fileName);
return true;
}
});
var presentation = new aspose.slides.Presentation("presentation.pptx");
try {
var options = new aspose.slides.MarkdownSaveOptions();
options.setExportType(aspose.slides.MarkdownExportType.Visual);
options.setBasePath(outputDirectory);
options.setImagesSaveFolderName("fallback-images");
options.setImageSaving(imageSavingHandler);
options.setSvgImageSaving(svgImageSavingHandler);
const markdownPath = path.join(outputDirectory, "presentation.md");
presentation.save(markdownPath, aspose.slides.SaveFormat.Md, options);
} finally {
presentation.dispose();
}
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.setImageSaving for emitted bitmap and metafile resources and MarkdownSaveOptions.setSvgImageSaving for resources emitted as SVG. The former provides an IImage object and an ImageFormat value; the latter provides an ISvgImage object whose SVG data can be read with ISvgImage.getSvgData. A source SVG that is rasterized during export is processed by the image-saving callback 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 the values set with MarkdownSaveOptions.setBasePath and MarkdownSaveOptions.setImagesSaveFolderName.
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[0], 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.join 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.