Wyszukiwanie i zamiana tekstu w prezentacjach PowerPoint w PHP

Przegląd

Aspose.Slides for PHP via Java może wyszukiwać, podświetlać i zamieniać tekst w pojedynczej ramce tekstowej lub w całej prezentacji. Każda operacja może także powiadamiać aplikację o każdym dopasowaniu za pomocą wywołania zwrotnego. Dzięki temu można aktualizować prezentację i jednocześnie tworzyć ścieżkę audytu zawierającą dopasowany tekst, jego kontekst, pozycję, ramkę tekstową oraz numer slajdu.

Narzędzia te są przydatne przy przeglądzie, redakcji, weryfikacji terminologii, czyszczeniu szablonów oraz zautomatyzowanych procesach raportowania.

W pierwszych przykładach poniżej używamy pliku o nazwie “sample.pptx”, który zawiera pojedyncze pole tekstowe na pierwszym slajdzie z następującym tekstem:

Przykładowy tekst

Wybierz zakres wyszukiwania

Użyj metod z klasy TextFrame aby ograniczyć operację do jednej ramki tekstowej. Użyj metod z klasy Presentation aby przetworzyć cały odpowiedni tekst w prezentacji.

Operacja Jedna ramka tekstowa Cała prezentacja
Podświetl tekst dosłowny TextFrame::highlightText Presentation::highlightText
Podświetl dopasowania wyrażenia regularnego TextFrame::highlightRegex Presentation::highlightRegex
Zamień tekst dosłowny TextFrame::replaceText Presentation::replaceText
Zamień dopasowania wyrażenia regularnego TextFrame::replaceRegex Presentation::replaceRegex

Skonfiguruj dopasowywanie tekstu

Dla operacji na tekście dosłownym użyj TextSearchOptions aby kontrolować dopasowanie:

Operacje z wyrażeniami regularnymi używają obiektu Java Pattern, więc reguły dopasowania takie jak rozróżnianie wielkości znaków i granice słów są definiowane w wyrażeniu i jego flagach.

Zidentyfikuj właściciela ramki tekstowej

Typowe przepływy przetwarzania tekstu często otrzymują obiekt TextFrame podczas wyszukiwania, zamiany, walidacji lub eksportu tekstu. Użyj TextFrame::getParentShape i TextFrame::getParentCell, aby określić, który obiekt prezentacji jest właścicielem ramki tekstowej.

Oczekiwane wartości zależą od właściciela:

Właściciel ramki tekstowej getParentShape getParentCell
AutoShape lub inny kształt zawierający tekst Właścielski Shape null
Komórka tabeli null Właścielski Cell

Obie metody zapewniają nawigację tylko do odczytu. Wywołanie ich nie przenosi ramki tekstowej ani nie zmienia jej właściciela. Kod ogólny powinien sprawdzać obie wartości przy pomocy java_is_null i obsługiwać możliwość, że żaden właściciel nie jest dostępny.

Poniższy przykład używa SlideUtil::getAllTextFrames do iteracji po ramach tekstowych w prezentacji. Dla kształtów wypisuje nazwę kształtu, typ Java oraz slajd, na którym się znajduje. Dla komórek tabeli wypisuje współrzędne kolumny i wiersza (liczone od zera) oraz slajd.

use aspose\slides\Presentation;
use aspose\slides\SlideUtil;

$presentation = new Presentation("presentation.pptx");
$arrayClass = new java_class("java.lang.reflect.Array");

try {
    $textFrames = SlideUtil::getAllTextFrames($presentation, false);
    $textFrameCount = java_values($arrayClass->getLength($textFrames));

    for ($textFrameIndex = 0; $textFrameIndex < $textFrameCount; $textFrameIndex++) {
        $textFrame = $textFrames[$textFrameIndex];
        $ownerShape = $textFrame->getParentShape();
        if (!java_is_null($ownerShape)) {
            $shapeName = java_values($ownerShape->getName());
            $shapeName = $shapeName === "" ? "(unnamed)" : $shapeName;
            $shapeType = java_values($ownerShape->getClass()->getSimpleName());
            $baseSlide = $ownerShape->getSlide();
            $slideClassName = java_values($baseSlide->getClass()->getName());

            if ($slideClassName === "com.aspose.slides.Slide") {
                $slideLabel = "slide " . java_values($baseSlide->getSlideNumber());
            } elseif ($slideClassName === "com.aspose.slides.NotesSlide") {
                $slideLabel = "notes for slide " . java_values($baseSlide->getParentSlide()->getSlideNumber());
            } else {
                $slideLabel = java_values($baseSlide->getClass()->getSimpleName());
            }

            echo("Shape: " . $shapeName . "; type: " . $shapeType . "; " . $slideLabel . "\n");
            continue;
        }

        $ownerCell = $textFrame->getParentCell();
        if (!java_is_null($ownerCell)) {
            $baseSlide = $ownerCell->getSlide();
            $slideClassName = java_values($baseSlide->getClass()->getName());

            if ($slideClassName === "com.aspose.slides.Slide") {
                $slideLabel = "slide " . java_values($baseSlide->getSlideNumber());
            } elseif ($slideClassName === "com.aspose.slides.NotesSlide") {
                $slideLabel = "notes for slide " . java_values($baseSlide->getParentSlide()->getSlideNumber());
            } else {
                $slideLabel = java_values($baseSlide->getClass()->getSimpleName());
            }

            echo("Table cell: column " . java_values($ownerCell->getFirstColumnIndex()) . ", row " . java_values($ownerCell->getFirstRowIndex()) . "; " . $slideLabel . "\n");
            continue;
        }

        echo("The text frame owner is not available as a shape or table cell.\n");
    }
} finally {
    $presentation->dispose();
}

Dla treści SmartArt iteruj po kształtach zwróconych przez SmartArtNode::getShapes i uzyskaj każdą SmartArtShape::getTextFrame. Ramka tekstowa może być powiązana z odpowiednim kształtem za pomocą TextFrame::getParentShape, natomiast TextFrame::getParentCell zwraca null. Dlatego gałąź dotycząca kształtów w przykładzie obsługuje również tekst z węzłów SmartArt.

Zbierz informacje o dopasowaniach przy użyciu wywołania zwrotnego

Przekaż wywołanie zwrotne proxy Java do metody podświetlającej lub zamieniającej, aby otrzymać powiadomienie o każdym dopasowaniu. Metoda wywołania zwrotnego otrzymuje powiązaną ramkę tekstową, tekst źródłowy, dopasowany tekst oraz pozycję dopasowania.

Wywołanie zwrotne nie otrzymuje numeru slajdu bezpośrednio. Implementacja poniżej wyprowadza go z rodzica slajdu i obsługuje także tekst znaleziony w notatkach slajdów. Tablica wynikowa używa null, gdy tekst jest powiązany z innym typem slajdu.

class TextSearchCallback {
    private $results = [];

    public function getResults() {
        return $this->results;
    }

    public function foundResult($textFrame, $sourceText, $foundText, $textPosition) {
        $slideNumber = $this->getSlideNumber($textFrame);
        $this->results[] = [
            "textFrame" => $textFrame,
            "sourceText" => java_values($sourceText),
            "foundText" => java_values($foundText),
            "textPosition" => java_values($textPosition),
            "slideNumber" => $slideNumber
        ];
    }

    private function getSlideNumber($textFrame) {
        $parentShape = $textFrame->getParentShape();
        $parentCell = $textFrame->getParentCell();

        if (!java_is_null($parentShape)) {
            $parentSlide = $parentShape->getSlide();
        } elseif (!java_is_null($parentCell)) {
            $parentSlide = $parentCell->getSlide();
        } else {
            $parentSlide = $textFrame->getSlide();
        }

        if (java_is_null($parentSlide)) {
            return null;
        }

        $parentSlideClass = $parentSlide->getClass();
        $classNameValue = $parentSlideClass->getName();
        $className = java_values($classNameValue);

        if ($className === "com.aspose.slides.Slide") {
            $slideNumber = $parentSlide->getSlideNumber();
            return java_values($slideNumber);
        }

        if ($className === "com.aspose.slides.NotesSlide") {
            $slide = $parentSlide->getParentSlide();
            $slideNumber = $slide->getSlideNumber();
            return java_values($slideNumber);
        }

        return null;
    }
}

Utwórz proxy dla tego obiektu PHP przed przekazaniem go do operacji:

$callbackHandler = new TextSearchCallback();
$callbackInterface = java("com.aspose.slides.IFindResultCallback");
$callback = java_closure(
    $callbackHandler,
    null,
    $callbackInterface
);

Dla operacji zamiany foundText zawiera pierwotny dopasowany tekst, więc wywołanie zwrotne może dokładnie zapisać, które terminy zostały zamienione.

Podświetl tekst

Użyj metody TextFrame::highlightText, aby podświetlić dopasowania tekstu dosłownego w ramce tekstowej. Przekaż TextSearchOptions, aby kontrolować wyszukiwanie.

Poniższy przykład podświetla wszystkie wystąpienia znaków “try”, a następnie podświetla tylko pełne słowo “to”.

$presentation = new Presentation("sample.pptx");
try {
    $slide = $presentation->getSlides()->get_Item(0);
    $shape = $slide->getShapes()->get_Item(0);
    $callbackHandler = new TextSearchCallback();
    $callbackInterface = java("com.aspose.slides.IFindResultCallback");
    $callback = java_closure(
        $callbackHandler,
        null,
        $callbackInterface
    );

    $substringSearchOptions = new TextSearchOptions();
    $substringSearchOptions->setCaseSensitive(false);
    $substringHighlightColor = new Java("java.awt.Color", 173, 216, 230);

    // Podświetl każde wystąpienie "try" w ramce tekstowej.
    $shape->getTextFrame()->highlightText(
        "try",
        $substringHighlightColor,
        $substringSearchOptions,
        $callback
    );

    $wholeWordSearchOptions = new TextSearchOptions();
    $wholeWordSearchOptions->setWholeWordsOnly(true);
    $wholeWordSearchOptions->setCaseSensitive(false);
    $wholeWordHighlightColor = new Java("java.awt.Color", 238, 130, 238);

    // Podświetl tylko pełne słowo "to".
    $shape->getTextFrame()->highlightText(
        "to",
        $wholeWordHighlightColor,
        $wholeWordSearchOptions,
        $callback
    );

    foreach ($callbackHandler->getResults() as $result) {
        echo(
            "Found '" . $result["foundText"] . "' at position " .
            $result["textPosition"] . " on slide " .
            $result["slideNumber"] . ".\n"
        );
    }

    $presentation->save("highlighted_text.pptx", SaveFormat::Pptx);
}
finally {
    $presentation->dispose();
}

Wynik:

Podświetlony tekst

Podświetl tekst przy użyciu wyrażeń regularnych

Metoda TextFrame::highlightRegex podświetla dopasowania tekstu znalezione przy pomocy wyrażenia regularnego w ramce tekstowej.

Poniższy kod podświetla wszystkie słowa zawierające co najmniej siedem znaków:

$presentation = new Presentation("sample.pptx");
try {
    $slide = $presentation->getSlides()->get_Item(0);
    $shape = $slide->getShapes()->get_Item(0);
    $regex = java("java.util.regex.Pattern")->compile("\\b[^\\s]{7,}\\b");
    $highlightColor = java("java.awt.Color")->YELLOW;

    $shape->getTextFrame()->highlightRegex($regex, $highlightColor, null);

    $presentation->save("highlighted_text_using_regex.pptx", SaveFormat::Pptx);
}
finally {
    $presentation->dispose();
}

Wynik:

Podświetlony tekst przy użyciu wyrażenia regularnego

Podświetl tekst w całej prezentacji

Użyj metod Presentation::highlightText i Presentation::highlightRegex, aby przeszukać wszystkie odpowiednie ramki tekstowe w prezentacji. Poniższy przykład podświetla dosłowne wyrażenie oraz wszystkie adresy e‑mail:

$presentation = new Presentation("presentation.pptx");
try {
    $searchOptions = new TextSearchOptions();
    $searchOptions->setWholeWordsOnly(true);
    $searchOptions->setCaseSensitive(false);
    $termHighlightColor = java("java.awt.Color")->ORANGE;

    $presentation->highlightText(
        "confidential",
        $termHighlightColor,
        $searchOptions,
        null
    );

    $patternClass = java("java.util.regex.Pattern");
    $emailPattern = "\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b";
    $emailRegex = $patternClass->compile(
        $emailPattern,
        $patternClass->CASE_INSENSITIVE
    );
    $emailHighlightColor = java("java.awt.Color")->YELLOW;

    $presentation->highlightRegex($emailRegex, $emailHighlightColor, null);
    $presentation->save("highlighted_presentation.pptx", SaveFormat::Pptx);
}
finally {
    $presentation->dispose();
}

Zamień tekst w ramce tekstowej

Użyj TextFrame::replaceText dla tekstu dosłownego i TextFrame::replaceRegex dla zamiany opartej na wzorcu. Metody te aktualizują dopasowany tekst wewnątrz istniejącej ramki, zachowując formatowanie otaczających fragmentów zamiast przebudowywać ramkę z czystego łańcucha.

Poniższy przykład standaryzuje wariant pisowni, a następnie zamienia etykiety wersji:

$presentation = new Presentation("presentation.pptx");
try {
    $slide = $presentation->getSlides()->get_Item(0);
    $shape = $slide->getShapes()->get_Item(0);

    $searchOptions = new TextSearchOptions();
    $searchOptions->setWholeWordsOnly(true);
    $searchOptions->setCaseSensitive(false);

    $shape->getTextFrame()->replaceText(
        "colour",
        "color",
        $searchOptions,
        null
    );

    $patternClass = java("java.util.regex.Pattern");
    $versionPattern = "\\bv\\d+(?:\\.\\d+)*\\b";
    $versionRegex = $patternClass->compile(
        $versionPattern,
        $patternClass->CASE_INSENSITIVE
    );
    $shape->getTextFrame()->replaceRegex(
        $versionRegex,
        "current version",
        null
    );

    $presentation->save("updated_text_frame.pptx", SaveFormat::Pptx);
}
finally {
    $presentation->dispose();
}

Jeśli jedno dopasowanie obejmuje fragmenty o różnym formatowaniu, sprawdź wynik, aby potwierdzić, które formatowanie powinno zostać zastosowane do tekstu zamiany.

Zamień tekst w całej prezentacji

Użyj metod Presentation::replaceText i Presentation::replaceRegex, aby zastosować te same operacje w całej prezentacji. Jest to przydatne przy czyszczeniu szablonów, aktualizacji terminologii i redakcji.

$presentation = new Presentation("presentation.pptx");
try {
    $searchOptions = new TextSearchOptions();
    $searchOptions->setWholeWordsOnly(true);
    $searchOptions->setCaseSensitive(true);

    $presentation->replaceText(
        "Contoso",
        "Example Corp",
        $searchOptions,
        null
    );

    $accountNumberRegex = java("java.util.regex.Pattern")->compile(
        "\\bACCT-\\d{6}\\b"
    );
    $presentation->replaceRegex(
        $accountNumberRegex,
        "ACCT-REDACTED",
        null
    );

    $presentation->save("updated_presentation.pptx", SaveFormat::Pptx);
}
finally {
    $presentation->dispose();
}

Grupuj dopasowania w raportach

Ponieważ każdy wynik przechowuje numer slajdu i ramkę tekstową, aplikacje mogą grupować dopasowania w celach audytu, raportowania lub przeglądu. Poniższy przykład grupuje zebrane wyniki najpierw według slajdu, a potem według ramki tekstowej:

$matchesBySlide = [];
$systemClass = java("java.lang.System");

foreach ($callbackHandler->getResults() as $result) {
    $slideNumber = $result["slideNumber"];
    $slideLabel = $slideNumber === null ? "Other" : (string) $slideNumber;
    $textFrame = $result["textFrame"];
    $textFrameHash = $systemClass->identityHashCode($textFrame);
    $textFrameKey = (string) java_values($textFrameHash);

    if (!isset($matchesBySlide[$slideLabel])) {
        $matchesBySlide[$slideLabel] = [];
    }

    if (!isset($matchesBySlide[$slideLabel][$textFrameKey])) {
        $matchesBySlide[$slideLabel][$textFrameKey] = [
            "textFrame" => $textFrame,
            "matches" => []
        ];
    }

    $matchesBySlide[$slideLabel][$textFrameKey]["matches"][] = $result;
}

foreach ($matchesBySlide as $slideLabel => $textFrameGroups) {
    echo("Slide: " . $slideLabel . "\n");

    foreach ($textFrameGroups as $textFrameGroup) {
        $textFrame = $textFrameGroup["textFrame"];
        echo("  Text frame: " . $textFrame->getText() . "\n");

        foreach ($textFrameGroup["matches"] as $result) {
            echo(
                "    '" . $result["foundText"] . "' at position " .
                $result["textPosition"] . "; context: '" .
                $result["sourceText"] . "'\n"
            );
        }
    }
}

FAQ

Jak mogę przeszukać tylko jedną ramkę tekstową zamiast całej prezentacji?

Uzyskaj ramkę tekstową kształtu i wywołaj TextFrame::highlightText, TextFrame::highlightRegex, TextFrame::replaceText lub TextFrame::replaceRegex na tej ramce. Metody na poziomie prezentacji przetwarzają wszystkie odpowiednie ramki tekstowe zamiast tego.

Jak dopasować pełne słowa z zachowaniem prawidłowej wielkości liter?

Ustaw TextSearchOptions::setWholeWordsOnly i TextSearchOptions::setCaseSensitive na true i przekaż opcje do metody podświetlającej lub zamieniającej tekst dosłowny. W przypadku wyrażeń regularnych określ granice słów i rozróżnianie wielkości znaków bezpośrednio w obiekcie Java Pattern.

Czy wyszukiwanie i zamiana mogą obejmować tekst w notatkach slajdów?

Tak. Ustaw TextSearchOptions::setIncludeNotes na true przy używaniu operacji na poziomie prezentacji dotyczącej tekstu dosłownego.

Jak stworzyć raport bez ponownego skanowania prezentacji?

Przekaż wywołanie zwrotne proxy Java do operacji podświetlania lub zamiany. Otrzymuje ono każde dopasowanie w trakcie działania operacji, dzięki czemu aplikacja może zapisać tekst źródłowy, dopasowany tekst, pozycję, ramkę tekstową oraz wyliczony numer slajdu do późniejszego grupowania lub eksportu.

Czy zamiana tekstu zachowuje jego formatowanie?

TextFrame::replaceText i TextFrame::replaceRegex modyfikują dopasowany tekst wewnątrz istniejącej ramki i zachowują formatowanie otaczających fragmentów. Jeśli dopasowanie obejmuje fragmenty o różnym formatowaniu, należy sprawdzić wynik, aby upewnić się, że zamiana używa pożądanego stylu.