Преобразовать презентации PowerPoint в Markdown в PHP

Обзор

Aspose.Slides for PHP via Java может конвертировать презентации PPT и PPTX в Markdown для документации, статических сайтов, миграции контента и рабочих процессов контроля версий. Вы можете выбрать вариант Markdown, управлять тем, как отображается содержимое слайдов, и решать, где сохранять экспортированные изображения и как сгенерированный Markdown будет на них ссылаться.

По умолчанию экспорт в Markdown использует только текстовый вывод. Чтобы экспортировать визуальное содержимое, установите тип экспорта с помощью метода MarkdownSaveOptions::setExportType в значение Sequential или Visual из перечисления MarkdownExportType. Sequential рендерит элементы слайдов отдельно и по порядку, тогда как Visual сохраняет сгруппированные элементы вместе, чтобы сохранить их визуальные отношения. Значение TextOnly не генерирует ресурсы изображений, поэтому обратные вызовы сохранения изображений не вызываются в этом режиме.

Преобразовать презентацию в Markdown

Загрузите исходный файл с помощью класса Presentation, а затем вызовите метод Presentation::save с параметром Md из перечисления SaveFormat.

use aspose\slides\Presentation;
use aspose\slides\SaveFormat;

$inputPath = __DIR__ . DIRECTORY_SEPARATOR . "presentation.pptx";
$outputPath = __DIR__ . DIRECTORY_SEPARATOR . "presentation.md";
$presentation = new Presentation($inputPath);
try {
    $presentation->save($outputPath, SaveFormat::Md);
} finally {
    $presentation->dispose();
}

Выбрать вариант Markdown

Метод MarkdownSaveOptions::setFlavor управляет спецификацией Markdown, используемой для вывода. Перечисление Flavor включает CommonMark, GitHub Flavored Markdown и другие поддерживаемые варианты.

Следующий пример экспортирует презентацию в формате CommonMark:

use aspose\slides\Flavor;
use aspose\slides\MarkdownSaveOptions;
use aspose\slides\Presentation;
use aspose\slides\SaveFormat;

$inputPath = __DIR__ . DIRECTORY_SEPARATOR . "presentation.pptx";
$outputPath = __DIR__ . DIRECTORY_SEPARATOR . "presentation.md";
$presentation = new Presentation($inputPath);
try {
    $options = new MarkdownSaveOptions();
    $options->setFlavor(Flavor::CommonMark);

    $presentation->save($outputPath, SaveFormat::Md, $options);
} finally {
    $presentation->dispose();
}

Экспорт изображений с использованием поведения сохранения по умолчанию

Класс MarkdownSaveOptions предоставляет два метода для настройки локально сохраняемых изображений:

  • setBasePath задает базовый каталог для документа Markdown и его ресурсов.
  • setImagesSaveFolderName задает подпапку для изображений. Значение по умолчанию — Images.

Следующий пример рендерит визуальное содержимое, записывает изображения в output/assets и создает относительные ссылки на изображения в документе Markdown:

use aspose\slides\MarkdownExportType;
use aspose\slides\MarkdownSaveOptions;
use aspose\slides\Presentation;
use aspose\slides\SaveFormat;

$inputPath = __DIR__ . DIRECTORY_SEPARATOR . "presentation.pptx";
$outputDirectory = __DIR__ . DIRECTORY_SEPARATOR . "output";
if (!is_dir($outputDirectory)) {
    mkdir($outputDirectory, 0777, true);
}

$presentation = new Presentation($inputPath);
try {
    $options = new MarkdownSaveOptions();
    $options->setExportType(MarkdownExportType::Visual);
    $options->setBasePath($outputDirectory);
    $options->setImagesSaveFolderName("assets");

    $markdownPath = $outputDirectory . DIRECTORY_SEPARATOR . "presentation.md";
    $presentation->save($markdownPath, SaveFormat::Md, $options);
} finally {
    $presentation->dispose();
}

Это поведение также используется в качестве резервного варианта, когда пользовательский обработчик сохранения изображений возвращает false.

Настройка сохранения изображений и ссылок Markdown

Используйте метод MarkdownSaveOptions::setImageSaving для регистрации обратного вызова для растровых и метафайловых ресурсов, не являющихся SVG, генерируемых при экспорте в Markdown. Его обратный вызов MarkdownImageSavingHandler получает объект IImage, значение ImageFormat и сгенерированную ссылку Markdown в виде массива Java строк из одного элемента. Сохраните или загрузите изображение в указанном формате и замените $link[0] ссылкой, которая должна появиться в выводе Markdown.

Ресурсы, генерируемые в формате SVG, обрабатываются отдельно. Зарегистрируйте обратный вызов с помощью метода MarkdownSaveOptions::setSvgImageSaving. Его обратный вызов MarkdownSvgImageSavingHandler получает объект ISvgImage и массив Java строк $link из одного элемента. У SVG нет аргумента ImageFormat; вместо этого запишите или загрузите его XML-данные через метод ISvgImage::getSvgData. В зависимости от режима экспорта и визуального группирования, SVG в исходной презентации может быть растеризован или объединён с другим содержимым; полученный не‑SVG ресурс затем передаётся в обратный вызов сохранения изображения. Регистрируйте оба обратных вызова, когда каждый экспортируемый визуальный ресурс требует пользовательской обработки.

В PHP через Java реализуйте каждый обратный вызов в PHP‑классе и используйте java_closure, чтобы открыть этот объект как соответствующий Java‑интерфейс.

Значение, возвращаемое обработчиком, определяет, кто будет обрабатывать изображение:

  • Вернуть true после того, как обработчик сохранил, загрузил, преобразовал или иначе обработал изображение и присвоил допустимое значение $link[0]. Aspose.Slides записывает это значение в документ Markdown и не выполняет своё сохранение по умолчанию.
  • Вернуть false, чтобы позволить Aspose.Slides сохранить изображение локально и сгенерировать его ссылку в соответствии со значениями, установленными через MarkdownSaveOptions::setBasePath и MarkdownSaveOptions::setImagesSaveFolderName.

Сохранить изображения в директорию CDN‑источника и использовать внешние URL

Следующий пример рассматривает cdn-origin/presentations/quarterly-report как смонтированную или синхронизированную директорию CDN‑источника. Каждый обработчик извлекает сгенерированное имя файла, сохраняет изображение в эту пользовательскую директорию и заменяет сгенерированную локальную ссылку публичным URL CDN. Сам пример не выполняет загрузку по сети: URL становится действительным только после монтирования директории как источника CDN или публикации её файлов в CDN. Для объектного хранилища замените запись в файловой системе операцией загрузки через SDK хранилища и присвойте $link[0] только после успешной загрузки.

use aspose\slides\MarkdownExportType;
use aspose\slides\MarkdownSaveOptions;
use aspose\slides\Presentation;
use aspose\slides\SaveFormat;

define("JAVA_PREFER_VALUES", 1);
require_once("http://localhost:8080/JavaBridge/java/Java.inc");
require_once("lib/aspose.slides.php");

function getFileNameFromLink($generatedLink)
{
    $urlCompatibleLink = str_replace("\\", "/", java_values($generatedLink));
    return basename($urlCompatibleLink);
}

function buildPublicUrl($publicBaseUrl, $fileName)
{
    return rtrim($publicBaseUrl, "/") . "/" . rawurlencode($fileName);
}

class CustomImageSavingHandler
{
    private $storageDirectory;
    private $publicBaseUrl;

    function __construct($storageDirectory, $publicBaseUrl)
    {
        $this->storageDirectory = $storageDirectory;
        $this->publicBaseUrl = $publicBaseUrl;
    }

    function invoke($image, $format, $link)
    {
        if (java_values($image->getWidth()) < 128 || java_values($image->getHeight()) < 128) {
            return false;
        }

        $fileName = getFileNameFromLink($link[0]);
        $storagePath = $this->storageDirectory . DIRECTORY_SEPARATOR . $fileName;
        $image->save($storagePath, $format);
        $link[0] = buildPublicUrl($this->publicBaseUrl, $fileName);
        return true;
    }
}

class CustomSvgImageSavingHandler
{
    private $storageDirectory;
    private $publicBaseUrl;

    function __construct($storageDirectory, $publicBaseUrl)
    {
        $this->storageDirectory = $storageDirectory;
        $this->publicBaseUrl = $publicBaseUrl;
    }

    function invoke($svgImage, $link)
    {
        $fileName = getFileNameFromLink($link[0]);
        $storagePath = $this->storageDirectory . DIRECTORY_SEPARATOR . $fileName;
        $outputStream = null;
        try {
            $outputStream = new Java("java.io.FileOutputStream", $storagePath);
            $outputStream->write($svgImage->getSvgData());
        } catch (Throwable $exception) {
            fwrite(STDERR, "Could not save the SVG image: " . $exception->getMessage() . PHP_EOL);
            return false;
        } finally {
            if ($outputStream !== null) {
                $outputStream->close();
            }
        }

        $link[0] = buildPublicUrl($this->publicBaseUrl, $fileName);
        return true;
    }
}

$inputPath = __DIR__ . DIRECTORY_SEPARATOR . "presentation.pptx";
$outputDirectory = __DIR__ . DIRECTORY_SEPARATOR . "output";
$publicBaseUrl = "https://cdn.example.com/presentations/quarterly-report";
$storageDirectory = __DIR__ . DIRECTORY_SEPARATOR . "cdn-origin" . DIRECTORY_SEPARATOR . "presentations" . DIRECTORY_SEPARATOR . "quarterly-report";
if (!is_dir($outputDirectory)) {
    mkdir($outputDirectory, 0777, true);
}
if (!is_dir($storageDirectory)) {
    mkdir($storageDirectory, 0777, true);
}

$presentation = new Presentation($inputPath);
try {
    $options = new MarkdownSaveOptions();
    $options->setExportType(MarkdownExportType::Visual);
    $options->setBasePath($outputDirectory);
    $options->setImagesSaveFolderName("fallback-images");

    $imageSavingHandler = java_closure(new CustomImageSavingHandler($storageDirectory, $publicBaseUrl), null, java('com.aspose.slides.MarkdownSaveOptions$MarkdownImageSavingHandler'));
    $svgImageSavingHandler = java_closure(new CustomSvgImageSavingHandler($storageDirectory, $publicBaseUrl), null, java('com.aspose.slides.MarkdownSaveOptions$MarkdownSvgImageSavingHandler'));
    $options->setImageSaving($imageSavingHandler);
    $options->setSvgImageSaving($svgImageSavingHandler);

    $markdownPath = $outputDirectory . DIRECTORY_SEPARATOR . "presentation.md";
    $presentation->save($markdownPath, SaveFormat::Md, $options);
} finally {
    $presentation->dispose();
}

Обработчик растровых изображений намеренно возвращает false для изображений меньше 128 × 128 пикселей, поэтому Aspose.Slides сохраняет такие изображения в output/fallback-images, используя поведение по умолчанию. Большие растровые и метафайловые ресурсы, а также SVG‑ресурсы обрабатываются пользовательским кодом. Например, сгенерированная локальная ссылка fallback-images/image1.png превращается в https://cdn.example.com/presentations/quarterly-report/image1.png. Обработчики используют пути операционной системы только при записи файлов; ссылки, записываемые в Markdown, используют прямые слэши и URL‑экранированные имена файлов. Применяйте то же правило при построении относительных ссылок: используйте /, а не разделитель каталогов, зависящий от платформы.

FAQ

Может ли один обработчик обрабатывать как растровые, так и SVG‑изображения?

Нет. Используйте MarkdownSaveOptions::setImageSaving для ресурсов, генерируемых как растровые и метафайлы, и MarkdownSaveOptions::setSvgImageSaving для ресурсов, генерируемых как SVG. Первый предоставляет объект IImage и значение ImageFormat; второй – объект ISvgImage, данные SVG которого можно прочитать с помощью ISvgImage::getSvgData. SVG‑файл, растеризованный во время экспорта, обрабатывается обратным вызовом сохранения изображения.

Что происходит, когда обработчик сохранения изображения возвращает false?

Aspose.Slides использует своё поведение сохранения по умолчанию. Расположение изображения и сгенерированная ссылка контролируются значениями, установленными с помощью MarkdownSaveOptions::setBasePath и MarkdownSaveOptions::setImagesSaveFolderName.

Может ли обработчик предоставить URL без локального сохранения изображения?

Да. Обработчик может загрузить изображение в объектное хранилище или передать его в другой сервис, присвоить полученный URL переменной $link[0] и вернуть true. Обработчик обязан завершить обработку самостоятельно; возврат true предотвращает сохранение по умолчанию.

Почему экспорт в Markdown бросает InvalidOperationException из обработчика?

Это исключение возникает, когда обработчик возвращает true, но не предоставляет корректную ссылку. Присвойте относительный путь или внешний URL, который должен быть записан в Markdown, перед возвратом true.

Какой разделитель пути следует использовать в ссылках на изображения?

Используйте прямые слэши в ссылках Markdown и URL. DIRECTORY_SEPARATOR применяйте только для путей файловой системы, а затем отдельно формируйте или нормализуйте ссылку Markdown.

Сохраняются ли гиперссылки при экспорте в Markdown?

Да. Текстовые hyperlinks сохраняются как обычные ссылки Markdown. Переходы слайдов transitions и animations не конвертируются.

Можно ли конвертировать презентации в Markdown параллельно?

Можно обрабатывать разные файлы презентаций параллельно, но не делитесь одним экземпляром Presentation между потоками. Следуйте multithreading guidelines и используйте отдельный экземпляр для каждого файла.