Управление гиперссылками презентации в JavaScript

Введение

Гиперссылка соединяет содержимое презентации с веб‑сайтом или местом внутри презентации. В PowerPoint гиперссылки обычно служат двум целям:

  • Открыть веб‑сайт из текста, формы или медиа‑кадра.
  • Перейти к другому слайду, например, из оглавления.

Aspose.Slides for Node.js via Java позволяет добавлять такие ссылки, управлять их внешним видом и звуком, обновлять их свойства и удалять их. Приведённые ниже примеры показывают, как работать с гиперссылками на отдельных элементах и как получать доступ к гиперссылкам на уровне презентации, слайда или текстового кадра.

Добавить URL‑гиперссылки

Вы можете присвоить URL‑адрес веб‑сайта тексту, форме или медиа‑кадру. Элемент, к которому вы присваиваете гиперссылку, определяет область клика: текстовая часть связывает выбранный текст, тогда как форма или кадр связывают объект слайда.

Для презентаций, содержащих относительные гиперссылки, см. Управление базой гиперссылок, чтобы прочитать, изменить или очистить базовый уровень документа.

Добавить URL‑гиперссылки к тексту

Чтобы привязать текст к веб‑сайту, передайте объект Hyperlink в метод setHyperlinkClick текстовой части, как показано ниже. Только эта часть текста станет кликабельной.

const aspose = { slides: require("aspose.slides.via.java") };

const presentation = new aspose.slides.Presentation();
try {
    const textShape = presentation.getSlides().get_Item(0).getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 100, 100, 600, 50, false);
    textShape.addTextFrame("Aspose: File Format APIs");
    const portionFormat = textShape.getTextFrame().getParagraphs().get_Item(0).getPortions().get_Item(0).getPortionFormat();
    portionFormat.setHyperlinkClick(new aspose.slides.Hyperlink("https://www.aspose.com/"));
    portionFormat.getHyperlinkClick().setTooltip("Explore Aspose file format APIs");
    portionFormat.setFontHeight(32);

    presentation.save("presentation-out.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Добавить URL‑гиперссылки к формам и медиа‑кадрам

Чтобы сделать форму или кадр кликабельным, вызовите его метод setHyperlinkClick. Гиперссылка принадлежит самому объекту, а не текстовой части внутри него.

Та же методика применяется к изображениями, аудио‑ и видеокадрам: присвойте гиперссылку кадру и при необходимости вызовите setTooltip.

Следующий пример делает прямоугольник кликабельным:

const aspose = { slides: require("aspose.slides.via.java") };

const presentation = new aspose.slides.Presentation();
try {
    const shape = presentation.getSlides().get_Item(0).getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 100, 100, 600, 50);

    shape.setHyperlinkClick(new aspose.slides.Hyperlink("https://www.aspose.com/"));
    shape.getHyperlinkClick().setTooltip("Explore Aspose file format APIs");

    presentation.save("presentation-out.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Использовать гиперссылки для создания оглавления

Внутренние гиперссылки позволяют читателям переходить из оглавления к конкретному слайду. В следующем примере используется setInternalHyperlinkClick для связывания текста «Page 2» на первом слайде со вторым слайдом.

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const firstSlide = presentation.getSlides().get_Item(0);
    const secondSlide = presentation.getSlides().addEmptySlide(firstSlide.getLayoutSlide());

    const tableOfContents = firstSlide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 40, 40, 300, 100);
    tableOfContents.getFillFormat().setFillType(java.newByte(aspose.slides.FillType.NoFill));
    tableOfContents.getLineFormat().getFillFormat().setFillType(java.newByte(aspose.slides.FillType.NoFill));
    tableOfContents.getTextFrame().getParagraphs().clear();

    const paragraph = new aspose.slides.Paragraph();
    paragraph.getParagraphFormat().getDefaultPortionFormat().getFillFormat().setFillType(java.newByte(aspose.slides.FillType.Solid));
    paragraph.getParagraphFormat().getDefaultPortionFormat().getFillFormat().getSolidFillColor().setColor(java.getStaticFieldValue("java.awt.Color", "BLACK"));
    paragraph.setText("Title of slide 2 .......... ");

    const linkPortion = new aspose.slides.Portion();
    linkPortion.setText("Page 2");
    linkPortion.getPortionFormat().getHyperlinkManager().setInternalHyperlinkClick(secondSlide);

    paragraph.getPortions().add(linkPortion);
    tableOfContents.getTextFrame().getParagraphs().add(paragraph);

    presentation.save("link_to_slide.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Форматировать гиперссылки

Цвет

Метод setColorSource класса Hyperlink определяет, использует ли гиперссылка цвет гиперссылки презентации или форматирование текстовой части. Чтобы применить пользовательский цвет текста, выберите HyperlinkColorSource.PortionFormat и установите цвет заливки части. Эта возможность была введена в PowerPoint 2019; в более ранних версиях настройка не применяется.

Следующий пример добавляет две текстовые гиперссылки на один слайд. Первая использует красный цвет текста, а вторая сохраняет стандартный цвет гиперссылки.

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const coloredShape = presentation.getSlides().get_Item(0).getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 100, 100, 450, 50, false);
    coloredShape.addTextFrame("This hyperlink uses a custom color.");
    const coloredPortionFormat = coloredShape.getTextFrame().getParagraphs().get_Item(0).getPortions().get_Item(0).getPortionFormat();
    coloredPortionFormat.setHyperlinkClick(new aspose.slides.Hyperlink("https://www.aspose.com/"));
    coloredPortionFormat.getHyperlinkClick().setColorSource(aspose.slides.HyperlinkColorSource.PortionFormat);
    coloredPortionFormat.getFillFormat().setFillType(java.newByte(aspose.slides.FillType.Solid));
    coloredPortionFormat.getFillFormat().getSolidFillColor().setColor(java.getStaticFieldValue("java.awt.Color", "RED"));

    const defaultShape = presentation.getSlides().get_Item(0).getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 100, 200, 450, 50, false);
    defaultShape.addTextFrame("This hyperlink uses the default color.");
    defaultShape.getTextFrame().getParagraphs().get_Item(0).getPortions().get_Item(0).getPortionFormat().setHyperlinkClick(new aspose.slides.Hyperlink("https://www.aspose.com/"));

    presentation.save("presentation-out-hyperlink.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Звук

Гиперссылка может воспроизводить звук при активации или останавливать уже проигрывающий звук. Используйте следующие методы для настройки этих поведений:

  • Hyperlink.setSound указывает аудио, связанное с гиперссылкой.
  • Hyperlink.setStopSoundOnClick управляет тем, останавливает ли активация гиперссылки предыдущий звук.

Добавить звук к гиперссылке

Следующий пример загружает sampleaudio.wav и привязывает его к кнопке на первом слайде. Нажатие кнопки воспроизводит звук и переходит к следующему слайду. Вторая форма на этом слайде останавливает предыдущий звук при нажатии, не выполняя перехода.

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");

const presentation = new aspose.slides.Presentation();
try {
    const audioStream = java.newInstanceSync("java.io.FileInputStream", "sampleaudio.wav");
    let hyperlinkSound;
    try {
        hyperlinkSound = presentation.getAudios().addAudio(audioStream);
    } finally {
        audioStream.close();
    }

    const firstSlide = presentation.getSlides().get_Item(0);

    const playButton = firstSlide.getShapes().addAutoShape(aspose.slides.ShapeType.SoundButton, 100, 100, 100, 50);
    playButton.setHyperlinkClick(aspose.slides.Hyperlink.getNextSlide());

    if (!playButton.getHyperlinkClick().getStopSoundOnClick() && playButton.getHyperlinkClick().getSound() == null)
    {
        playButton.getHyperlinkClick().setSound(hyperlinkSound);
    }

    const secondSlide = presentation.getSlides().addEmptySlide(firstSlide.getLayoutSlide());

    const stopButton = secondSlide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 100, 100, 100, 50);
    stopButton.setHyperlinkClick(aspose.slides.Hyperlink.getNoAction());

    stopButton.getHyperlinkClick().setStopSoundOnClick(true);

    presentation.save("hyperlink-sound.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Извлечь звук из гиперссылки

Следующий пример открывает созданную выше презентацию и считывает аудио гиперссылки первой формы в память с помощью getSound и getBinaryData.

const aspose = { slides: require("aspose.slides.via.java") };

const presentation = new aspose.slides.Presentation("hyperlink-sound.pptx");
try {
    if (presentation.getSlides().size() > 0 && presentation.getSlides().get_Item(0).getShapes().size() > 0) {
        const hyperlink = presentation.getSlides().get_Item(0).getShapes().get_Item(0).getHyperlinkClick();
        const sound = hyperlink == null ? null : hyperlink.getSound();
        if (sound != null) {
            const audioData = sound.getBinaryData();
            console.log("Extracted " + audioData.length + " bytes of hyperlink audio.");
        } else {
            console.log("The first shape has no hyperlink sound.");
        }
    } else {
        console.log("The presentation has no first slide or shape to inspect.");
    }
} finally {
    presentation.dispose();
}

Подсказка и настройки взаимодействия

После присвоения гиперссылки тексту или форме вы можете вызвать следующие методы класса Hyperlink:

  • setTooltip задаёт текст, который просмотрщик может отобразить в виде подсказки для ссылки.
  • setTargetFrame указывает целевой кадр внутри родительского HTML‑фреймсета, если применимо.
  • setHistory контролирует, будет ли активация ссылки добавлять её пункт назначения в список просмотренных гиперссылок.
  • setHighlightClick контролирует, будет ли гиперссылка выделяться при щелчке.

Удалить гиперсылки из презентаций

Используйте getAnyHyperlinks для сбора контейнеров гиперссылок, включая ссылки текстовых частей, перед их изменением. Следующий пример удаляет оба типа активации с первого слайда. Чтобы удалить только один тип, вызовите только removeHyperlinkClick или removeHyperlinkMouseOver; удаление действия щелчка не удаляет его аналог при наведении.

const aspose = { slides: require("aspose.slides.via.java") };

const presentation = new aspose.slides.Presentation("pres.pptx");
try {
    if (presentation.getSlides().size() > 0) {
        const found = presentation.getSlides().get_Item(0).getHyperlinkQueries().getAnyHyperlinks();
        const containers = [];
        for (let index = 0; index < found.size(); index++) {
            containers.push(found.get_Item(index));
        }
        for (const container of containers) {
            container.getHyperlinkManager().removeHyperlinkClick();
            container.getHyperlinkManager().removeHyperlinkMouseOver();
        }
        presentation.save("pres-removed-hyperlinks.pptx", aspose.slides.SaveFormat.Pptx);
    } else {
        console.log("The presentation has no slides to process.");
    }
} finally {
    presentation.dispose();
}

Для безусловного удаления removeAllHyperlinks удаляет оба типа активации в выбранном диапазоне одним вызовом. Для выборочной очистки и охвата мастеров, макетов и заметок см. Отчёт, очистка и проверка гиперссылок.

Создать полный инвентарь гиперссылок

Перед распространением презентации зафиксируйте её интерактивные действия, а также веб‑ссылки. getAnyHyperlinks возвращает контейнеры гиперссылок, а не простой список строк URL. Проверяйте как getHyperlinkClick, так и getHyperlinkMouseOver для каждого контейнера. Они независимы: один контейнер может иметь оба действия, поэтому полный отчёт требует до двух строк на контейнер.

Сканирование только гиперссылок уровня формы может пропустить ссылки, привязанные к текстовым частям. Запрашивайте соответствующий диапазон и сохраняйте полученные контейнеры, чтобы позже обновить или удалить их действия.

Запрос презентации, слайда и областей текстового кадра

Класс HyperlinkQueries доступен через Presentation.getHyperlinkQueries, BaseSlide.getHyperlinkQueries и TextFrame.getHyperlinkQueries. Каждый диапазон поддерживает одинаковые запросы:

  • getHyperlinkClicks возвращает контейнеры с действием щелчка.
  • getHyperlinkMouseOvers возвращает контейнеры с действием наведения.
  • getAnyHyperlinks возвращает контейнеры с одним или обоими действиями.

Следующий пример создаёт hyperlink-audit-input.pptx с внешней ссылкой‑щелчком, файлом‑наведением, внутренней навигацией по слайдам, текстовым наведением и макрос‑действием. Он не выполняет эти действия. Три запроса работают на каждом диапазоне; их подсчёты отражают количество контейнеров, а не суммарное число действий. Диапазон текстового кадра исключает ссылки самой окружающей формы.

const aspose = { slides: require("aspose.slides.via.java") };

function printQueryCounts(scope, queries) {
const clickCount = queries.getHyperlinkClicks().size();
const mouseOverCount = queries.getHyperlinkMouseOvers().size();
const anyCount = queries.getAnyHyperlinks().size();
console.log(scope + ": click=" + clickCount + ", mouse-over=" + mouseOverCount + ", any=" + anyCount);
}

const presentation = new aspose.slides.Presentation();
try {
    const slide = presentation.getSlides().get_Item(0);
    const destination = presentation.getSlides().addEmptySlide(slide.getLayoutSlide());
    const shape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 20, 20, 400, 60);
    shape.getTextFrame().setText("Click the text to go to slide 2");
    shape.getHyperlinkManager().setExternalHyperlinkClick("https://example.com/");
    shape.getHyperlinkClick().setTooltip("Public website");
    shape.getHyperlinkManager().setExternalHyperlinkMouseOver("file:///C:/private/report.xlsx");

    const portionFormat = shape.getTextFrame().getParagraphs().get_Item(0).getPortions().get_Item(0).getPortionFormat();
    portionFormat.getHyperlinkManager().setInternalHyperlinkClick(destination);
    portionFormat.getHyperlinkManager().setExternalHyperlinkMouseOver("https://example.com/help");
    const macroButton = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 20, 120, 200, 60);
    macroButton.getHyperlinkManager().setMacroHyperlinkClick("ReviewPresentation");

    printQueryCounts("Presentation", presentation.getHyperlinkQueries());
    printQueryCounts("Slide 1", slide.getHyperlinkQueries());
    printQueryCounts("Text frame", shape.getTextFrame().getHyperlinkQueries());
    presentation.save("hyperlink-audit-input.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

Для этого примера запросы презентации и слайда каждый возвращают три контейнера‑щелчка, два контейнера‑наведения и три контейнера с любым действием. Запрос текстового кадра возвращает по одному контейнеру в каждой категории.

Классифицировать действия и назначения

Используйте Hyperlink.getActionType для интерпретации действия перед определением его назначения. Значения HyperlinkActionType охватывают больше, чем веб‑навигацию:

Values Meaning for an audit
Hyperlink Внешняя гиперссылка; проверьте URL и его схему.
JumpSpecificSlide Внутренняя навигация к конкретному слайду.
JumpFirstSlide, JumpPreviousSlide, JumpNextSlide, JumpLastSlide, JumpLastViewedSlide Встроенная навигация по слайд‑шоу, определяемая в контексте слайд‑шоу.
JumpEndShow, StartCustomSlideShow Завершить текущее шоу или запустить пользовательское шоу.
StartMacro Выполнить макрос.
StartProgram Запустить программу.
OpenFile, OpenPresentation Открыть файл или другую презентацию; рассматривать отдельно от веб‑URL.
StartStopMedia Запустить или остановить воспроизведение медиа.
NoAction, Unknown Нет действия навигации, или неизвестное действие, требующее проверки.

Читайте внешние назначения через getExternalUrl и конкретные внутренние назначения через getTargetSlide. Внутренние действия и встроенные команды могут не иметь внешнего URL; пустой URL не означает отсутствие действия у контейнера. Сохраняйте значение, полученное от getExternalUrlOriginal, если оно отличается от нормализованного URL, и включайте подсказку из getTooltip, когда она доступна.

Отчёт, очистка и проверка гиперссылок

Следующий пример JavaScript читает существующую презентацию (используйте файл, созданный выше), записывает hyperlink-audit.json, применяет политику, сохраняет hyperlink-sanitized.pptx и снова открывает её для повторной проверки обоих типов активации. Он собирает контейнеры до их изменения и использует сравнение по ссылке, чтобы не обрабатывать один и тот же контейнер дважды. Запросы презентации охватывают обычные слайды; для инвентаризации всего пакета они также явно запрашивают мастера, макеты, заметки и мастера заметок/раздаточных материалов, если они присутствуют.

Отчёт фиксирует индекс слайда, начинающийся с единицы, и getSlideId, когда он доступен. getSlide предоставляет слайд‑владельца для поддерживаемых контейнеров. У мастеров, макетов и заметок нет обычного индекса слайда и они идентифицируются по своему диапазону. Контейнеры формы и контейнеры форматирования текстовых частей помечаются отдельно; другие типы контейнеров сохраняют своё имя типа во время выполнения. Каждый контейнер получает локальный идентификатор отчёта, чтобы можно было сопоставить его два действия. В отчёте типы действий сохраняются как целочисленные константы, определённые перечислением HyperlinkActionType.

Эта преднамеренно строгая политика приложения разрешает только абсолютные HTTPS‑URL и действительные внутренние цели слайдов. Она отклоняет макросы, программы, действия с файлами, другие действия слайд‑шоу, неизвестные действия и другие схемы URL. Эти отклонения — решения политики, а не вывод о безопасности Aspose.Slides. Один лишь HTTPS не гарантирует доверие: добавьте списки разрешённых хостов и другие проверки для вашего приложения. Проверяются как оригинальные, так и нормализованные внешние URL. Пример проверяет метаданные без переходов по ссылкам или выполнения действий.

Для исправления контейнерный метод getHyperlinkManager поддерживает setExternalHyperlinkClick, removeHyperlinkClick и removeHyperlinkMouseOver. Здесь запрещённые внешние ссылки‑щелчки заменяются фиксированной HTTPS‑страницей‑приёмником; остальные запрещённые щелчки и действия наведения удаляются независимо. Установите replaceExternalClicks в false, чтобы полностью удалить все нарушения политики. Выберите страницу‑замену, управляемую приложением, перед развертыванием.

Флаг экспорта отчёта использует консервативную политику проверки PDF: помечайте действия наведения и всё, что не является внешней ссылкой или переходом к конкретному слайду, как потенциально неподдерживаемое. Это подсказка для проверки, а не тест возможностей или гарантия, что непомеченные ссылки сохранятся при экспорте. Поддерживаемый PDF и HTML экспорты могут сохранять гиперссылки, в зависимости от действия, параметров экспорта и среды просмотра. Растровые изображения и видео не могут сохранять интерактивные гиперссылки; помечайте каждое действие при аудите для этих форматов вывода.

const aspose = { slides: require("aspose.slides.via.java") };
const java = require("java");
const fs = require("fs");

function slideIndex(presentation, slide) {
    if (slide == null) return null;
    for (let index = 0; index < presentation.getSlides().size(); index++) {
        if (presentation.getSlides().get_Item(index).equals(slide)) return index + 1;
    }
    return null;
}

function isHttps(value) {
    if (value == null || value.length === 0) return false;
    try {
        const uri = java.newInstanceSync("java.net.URI", value);
        const scheme = uri.getScheme();
        return uri.isAbsolute() && scheme != null && scheme.toLowerCase() === "https" && uri.getHost() != null;
    } catch (exception) {
        return false;
    }
}

function policyViolation(link) {
    if (link == null) return null;
    if (link.getActionType() === aspose.slides.HyperlinkActionType.JumpSpecificSlide) {
        return link.getTargetSlide() == null ? "Missing target slide" : null;
    }
    if (link.getActionType() !== aspose.slides.HyperlinkActionType.Hyperlink) return "Action is not allowed";
    if (!isHttps(link.getExternalUrl())) return "Normalized URL is not absolute HTTPS";
    const original = link.getExternalUrlOriginal();
    if (original != null && original.length > 0 && !isHttps(original)) return "Original URL is not absolute HTTPS";
    return null;
}

function collectContainers(presentation) {
    const found = [];
    function addQueries(queries) {
        const containers = queries.getAnyHyperlinks();
        for (let index = 0; index < containers.size(); index++) {
            found.push(containers.get_Item(index));
        }
    }
    function addScope(slide) {
        if (slide != null) addQueries(slide.getHyperlinkQueries());
    }
    addQueries(presentation.getHyperlinkQueries());
    for (let index = 0; index < presentation.getMasters().size(); index++) {
        addScope(presentation.getMasters().get_Item(index));
    }
    for (let index = 0; index < presentation.getLayoutSlides().size(); index++) {
        addScope(presentation.getLayoutSlides().get_Item(index));
    }
    for (let index = 0; index < presentation.getSlides().size(); index++) {
        addScope(presentation.getSlides().get_Item(index).getNotesSlideManager().getNotesSlide());
    }
    addScope(presentation.getMasterNotesSlideManager().getMasterNotesSlide());
    addScope(presentation.getMasterHandoutSlideManager().getMasterHandoutSlide());
    const seen = java.newInstanceSync("java.util.IdentityHashMap");
    const unique = [];
    for (const container of found) {
        if (!seen.containsKey(container)) {
            seen.put(container, true);
            unique.push(container);
        }
    }
    return unique;
}

function addRow(rows, presentation, link, activation, container, containerId) {
    if (link == null) return;
    const ownerSlide = java.instanceOf(container, "com.aspose.slides.ISlideComponent") ? container.getSlide() : null;
    const targetSlide = link.getTargetSlide();
    const violation = policyViolation(link);
    const ownerType = java.instanceOf(container, "com.aspose.slides.IShape") ? "Shape" : java.instanceOf(container, "com.aspose.slides.IPortionFormat") ? "Text portion" : container.getClass().getSimpleName();
    const ordinaryAction = link.getActionType() === aspose.slides.HyperlinkActionType.Hyperlink || link.getActionType() === aspose.slides.HyperlinkActionType.JumpSpecificSlide;
    rows.push({
        ContainerId: containerId,
        SlideIndex: slideIndex(presentation, ownerSlide),
        SlideId: ownerSlide == null ? null : ownerSlide.getSlideId(),
        Scope: ownerSlide == null ? null : ownerSlide.getClass().getSimpleName(),
        OwnerType: ownerType,
        Activation: activation,
        ActionType: link.getActionType(),
        ExternalUrl: link.getExternalUrl(),
        TargetSlideIndex: slideIndex(presentation, targetSlide),
        TargetSlideId: targetSlide == null ? null : targetSlide.getSlideId(),
        Tooltip: link.getTooltip(),
        OriginalExternalUrl: link.getExternalUrlOriginal() === link.getExternalUrl() ? null : link.getExternalUrlOriginal(),
        PotentiallyUnsafe: violation != null,
        PolicyViolation: violation,
        TargetExport: "PDF",
        PotentiallyUnsupportedByExport: activation === "mouse-over" || !ordinaryAction
    });
}

const replaceExternalClicks = true;
const replacementUrl = "https://example.com/blocked-link";
const presentation = new aspose.slides.Presentation("hyperlink-audit-input.pptx");
try {
    const containers = collectContainers(presentation);
    const rows = [];
    for (let index = 0; index < containers.length; index++) {
        const container = containers[index];
        addRow(rows, presentation, container.getHyperlinkClick(), "click", container, index + 1);
        addRow(rows, presentation, container.getHyperlinkMouseOver(), "mouse-over", container, index + 1);
    }
    const json = JSON.stringify(rows, null, 2);
    fs.writeFileSync("hyperlink-audit.json", json, "utf8");

    for (const container of containers) {
        const click = container.getHyperlinkClick();
        if (policyViolation(click) != null) {
            if (replaceExternalClicks && click.getActionType() === aspose.slides.HyperlinkActionType.Hyperlink) {
                container.getHyperlinkManager().setExternalHyperlinkClick(replacementUrl);
            } else {
                container.getHyperlinkManager().removeHyperlinkClick();
            }
        }
        if (policyViolation(container.getHyperlinkMouseOver()) != null) {
            container.getHyperlinkManager().removeHyperlinkMouseOver();
        }
    }
    presentation.save("hyperlink-sanitized.pptx", aspose.slides.SaveFormat.Pptx);

    const reopened = new aspose.slides.Presentation("hyperlink-sanitized.pptx");
    try {
        const remainingContainers = collectContainers(reopened);
        let violations = 0;
        for (const container of remainingContainers) {
            if (policyViolation(container.getHyperlinkClick()) != null) violations++;
            if (policyViolation(container.getHyperlinkMouseOver()) != null) violations++;
        }
        console.log("Audit rows: " + rows.length + "; prohibited actions after reopening: " + violations);
        if (violations !== 0) {
            console.log("Verification failed: do not distribute the saved presentation.");
        }
    } finally {
        reopened.dispose();
    }
} finally {
    presentation.dispose();
}

С созданным выше вводом отчёт содержит пять строк действий. Ссылка‑наведение к файлу и макрос‑щелчок удалены, тогда как HTTPS‑ссылки и внутренняя навигация по слайдам остались. Проверка выводит ноль запрещённых действий. Ввод, содержащий запрещённый внешний URL‑щелчок, также демонстрирует ветку замены. Контейнер с разрешённым щелчком и запрещённым наведением сохраняет своё действие‑щелчок.

Эта выборочная очистка отличается от removeAllHyperlinks, который удаляет оба типа активации во всём выбранном диапазоне независимо от политики. Проверка здесь проверяет только действия гиперссылок; она не удаляет встроенные VBA‑проекты, OLE‑объекты или другой активный контент и не проверяет экспортированный PDF или HTML файл.

Часто задаваемые вопросы

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

Разделы в PowerPoint группируют слайды, но внутренняя гиперссылка направлена на отдельный слайд. Чтобы реализовать навигацию к разделу, привяжите ссылку к первому слайду этого раздела.

Могу ли я прикрепить гиперссылку к элементам макетного слайда, чтобы она работала на всех слайдах?

Да. Элементы мастера и макета поддерживают гиперссылки. Ссылки на этих элементах доступны во время показа слайдов на всех слайдах, использующих соответствующий мастер или макет.

Сохранятся ли гиперссылки при экспорте в PDF, HTML, изображения или видео?

Поддерживаемый PDF и HTML экспорт могут сохранять гиперссылки; растровые изображения и видео этого не делают. См. особенности экспорта в разделе Отчёт, очистка и проверка гиперссылок.