diff --git a/samples/Sample_Header.php b/samples/Sample_Header.php index 57bb10a4c6..667db89ef6 100644 --- a/samples/Sample_Header.php +++ b/samples/Sample_Header.php @@ -31,7 +31,7 @@ } // Set writers -$writers = ['Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf', 'HTML' => 'html', 'PDF' => 'pdf', 'EPub3' => 'epub']; +$writers = ['Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf', 'HTML' => 'html', 'PDF' => 'pdf', 'EPub3' => 'epub', 'WPS' => 'wps']; // Set PDF renderer if (null === Settings::getPdfRendererPath()) { diff --git a/src/PhpWord/IOFactory.php b/src/PhpWord/IOFactory.php index 50c419cae2..7579a62fdc 100644 --- a/src/PhpWord/IOFactory.php +++ b/src/PhpWord/IOFactory.php @@ -36,7 +36,7 @@ abstract class IOFactory */ public static function createWriter(PhpWord $phpWord, $name = 'Word2007') { - if ($name !== 'WriterInterface' && !in_array($name, ['ODText', 'RTF', 'Word2007', 'HTML', 'PDF', 'EPub3'], true)) { + if ($name !== 'WriterInterface' && !in_array($name, ['ODText', 'RTF', 'Word2007', 'HTML', 'PDF', 'EPub3', 'WPS'], true)) { throw new Exception("\"{$name}\" is not a valid writer."); } diff --git a/src/PhpWord/Writer/WPS.php b/src/PhpWord/Writer/WPS.php new file mode 100644 index 0000000000..52afba1c78 --- /dev/null +++ b/src/PhpWord/Writer/WPS.php @@ -0,0 +1,117 @@ +setPhpWord($phpWord); + + // Create parts + $this->parts = [ + 'Content' => 'content.xml', + 'Styles' => 'styles.xml', + 'Meta' => 'meta.xml', + 'Manifest' => 'META-INF/manifest.xml', + ]; + foreach (array_keys($this->parts) as $partName) { + $partClass = "PhpOffice\\PhpWord\\Writer\\WPS\\Part\\{$partName}"; + if (class_exists($partClass)) { + /** @var AbstractPart $part */ + $part = new $partClass(); + $part->setParentWriter($this); + $this->writerParts[strtolower($partName)] = $part; + } + } + + // Set package paths + $this->mediaPaths = ['image' => 'Pictures/']; + } + + /** + * Save PhpWord to file. + */ + public function save(string $filename): void + { + $filename = $this->getTempFile($filename); + $zip = $this->getZipArchive($filename); + $phpWord = $this->getPhpWord(); + + // Clear any previous media elements + Media::clearElements(); + + // Collect media relations by traversing the document + foreach ($phpWord->getSections() as $section) { + Media::collectMediaRelations('section', $section); + foreach ($section->getHeaders() as $header) { + Media::collectMediaRelations('header', $header); + } + foreach ($section->getFooters() as $footer) { + Media::collectMediaRelations('footer', $footer); + } + } + + // Add collected media files to the package + $sectionMedia = Media::getElements('section'); + if (!empty($sectionMedia)) { + $this->addFilesToPackage($zip, $sectionMedia); + } + $headerMedia = Media::getElements('header'); + if (!empty($headerMedia)) { + $this->addFilesToPackage($zip, $headerMedia); + } + $footerMedia = Media::getElements('footer'); + if (!empty($footerMedia)) { + $this->addFilesToPackage($zip, $footerMedia); + } + + // Make sure required directories exist + $zip->addEmptyDir('Pictures'); // Ensure Pictures directory exists for images + $zip->addEmptyDir('META-INF'); + + // Write parts + foreach ($this->parts as $partName => $fileName) { + if ($fileName === '') { + continue; + } + $part = $this->getWriterPart($partName); + if (!$part instanceof AbstractPart) { + continue; + } + + $zip->addFromString($fileName, $part->write()); + } + + // Close zip archive and cleanup temp file + $zip->close(); + $this->cleanupTempFile(); + } +} diff --git a/src/PhpWord/Writer/WPS/Media.php b/src/PhpWord/Writer/WPS/Media.php new file mode 100644 index 0000000000..5b08d89df2 --- /dev/null +++ b/src/PhpWord/Writer/WPS/Media.php @@ -0,0 +1,105 @@ +> + */ + private static $elements = []; + + /** + * Add a media element to the collection. + * + * @param string $docPart e.g., 'section', 'header', 'footer' + * @param AbstractElement $element The media element (e.g., Image) + */ + public static function addElement(string $docPart, AbstractElement $element): void + { + if (!isset(self::$elements[$docPart])) { + self::$elements[$docPart] = []; + } + + if ($element instanceof Image) { + $mediaIndex = count(self::$elements[$docPart]) + 1; + $element->setMediaIndex($mediaIndex); + $element->setTarget("image{$mediaIndex}.{$element->getImageExtension()}"); + + self::$elements[$docPart][] = [ + 'type' => 'image', // Add the missing 'type' index + 'source' => $element->getSource(), + 'target' => $element->getTarget(), + 'isMemImage' => $element->isMemImage(), + 'imageString' => $element->isMemImage() ? $element->getImageString() : null, + ]; + } + // Add handling for other media types (OLEObject) if needed + } + + /** + * Get all media elements for a specific document part. + * + * @param string $docPart e.g., 'section', 'header', 'footer' + * + * @return array + */ + public static function getElements(string $docPart): array + { + return self::$elements[$docPart] ?? []; + } + + /** + * Clear all stored media elements. + */ + public static function clearElements(): void + { + self::$elements = []; + } + + /** + * Recursively collect media elements from a container. + * + * @param string $docPart The document part ('section', 'header', 'footer') + * @param AbstractContainer $container The container element to traverse + */ + public static function collectMediaRelations(string $docPart, AbstractContainer $container): void + { + foreach ($container->getElements() as $element) { + if ($element instanceof Image) { + self::addElement($docPart, $element); + } elseif ($element instanceof AbstractContainer) { + // Recursively check sub-containers + self::collectMediaRelations($docPart, $element); + } + // Add checks for other media types (OLEObject) if needed + } + } +} diff --git a/src/PhpWord/Writer/WPS/Part/AbstractPart.php b/src/PhpWord/Writer/WPS/Part/AbstractPart.php new file mode 100644 index 0000000000..bb31893635 --- /dev/null +++ b/src/PhpWord/Writer/WPS/Part/AbstractPart.php @@ -0,0 +1,76 @@ +parentWriter = $parentWriter; + } + + /** + * Get parent writer. + */ + public function getParentWriter(): AbstractWriter + { + return $this->parentWriter; + } + + /** + * Get XML Writer. + */ + protected function getXmlWriter(): XMLWriter + { + if (!$this->xmlWriter instanceof XMLWriter) { + $compatibility = Settings::hasCompatibility() ? 1 : 0; // Convert boolean to integer + $this->xmlWriter = new XMLWriter($compatibility); + } + + return $this->xmlWriter; + } + + /** + * Write part. + */ + abstract public function write(): string; +} diff --git a/src/PhpWord/Writer/WPS/Part/Content.php b/src/PhpWord/Writer/WPS/Part/Content.php new file mode 100644 index 0000000000..0b24f24a4a --- /dev/null +++ b/src/PhpWord/Writer/WPS/Part/Content.php @@ -0,0 +1,196 @@ +getParentWriter()->getPhpWord(); + $xmlWriter = $this->getXmlWriter(); + + // XML header + $xmlWriter->startDocument('1.0', 'UTF-8'); + + // office:document-content + $xmlWriter->startElement('office:document-content'); + $xmlWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'); + $xmlWriter->writeAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'); + $xmlWriter->writeAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'); + $xmlWriter->writeAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'); + $xmlWriter->writeAttribute('xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'); + $xmlWriter->writeAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'); + $xmlWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); + $xmlWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/'); + $xmlWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'); + $xmlWriter->writeAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'); + $xmlWriter->writeAttribute('xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0'); + $xmlWriter->writeAttribute('xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0'); + $xmlWriter->writeAttribute('xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0'); + $xmlWriter->writeAttribute('xmlns:math', 'http://www.w3.org/1998/Math/MathML'); + $xmlWriter->writeAttribute('xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0'); + $xmlWriter->writeAttribute('xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0'); + $xmlWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office'); + $xmlWriter->writeAttribute('xmlns:ooow', 'http://openoffice.org/2004/writer'); + $xmlWriter->writeAttribute('xmlns:oooc', 'http://openoffice.org/2004/calc'); + $xmlWriter->writeAttribute('xmlns:dom', 'http://www.w3.org/2001/xml-events'); + $xmlWriter->writeAttribute('xmlns:xforms', 'http://www.w3.org/2002/xforms'); + $xmlWriter->writeAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema'); + $xmlWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + $xmlWriter->writeAttribute('xmlns:rpt', 'http://openoffice.org/2005/report'); + $xmlWriter->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2'); + $xmlWriter->writeAttribute('office:version', '1.2'); + + // office:scripts + $xmlWriter->startElement('office:scripts'); + $xmlWriter->endElement(); + + // office:font-face-decls + $xmlWriter->startElement('office:font-face-decls'); + $xmlWriter->startElement('style:font-face'); + $xmlWriter->writeAttribute('style:name', 'Arial'); + $xmlWriter->writeAttribute('svg:font-family', 'Arial'); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + + // office:automatic-styles + $xmlWriter->startElement('office:automatic-styles'); + $xmlWriter->endElement(); + + // office:body + $xmlWriter->startElement('office:body'); + + // office:text + $xmlWriter->startElement('office:text'); + + // Write sections + $sections = $phpWord->getSections(); + foreach ($sections as $section) { + $this->writeSection($xmlWriter, $section); + } + + $xmlWriter->endElement(); // office:text + $xmlWriter->endElement(); // office:body + $xmlWriter->endElement(); // office:document-content + + return $xmlWriter->getData(); + } + + /** + * Write section. + */ + private function writeSection(XMLWriter $xmlWriter, Section $section): void + { + $xmlWriter->startElement('text:section'); + $xmlWriter->writeAttribute('text:style-name', 'Sect' . $section->getSectionId()); + $xmlWriter->writeAttribute('text:name', 'Section' . $section->getSectionId()); + + // Process all elements + $elements = $section->getElements(); + $this->writeElements($xmlWriter, $elements); + + $xmlWriter->endElement(); // text:section + } + + /** + * Write elements. + */ + private function writeElements(XMLWriter $xmlWriter, array $elements): void + { + foreach ($elements as $element) { + if ($element instanceof TextRun) { + $this->writeTextRun($xmlWriter, $element); + } elseif ($element instanceof Text) { + $this->writeText($xmlWriter, $element); + } elseif ($element instanceof Table) { + $this->writeTable($xmlWriter, $element); + } elseif ($element instanceof AbstractContainer) { + $this->writeElements($xmlWriter, $element->getElements()); + } + } + } + + /** + * Write text element. + */ + private function writeText(XMLWriter $xmlWriter, Text $text): void + { + $xmlWriter->startElement('text:p'); + $xmlWriter->writeRaw($text->getText()); + $xmlWriter->endElement(); + } + + /** + * Write text run element. + */ + private function writeTextRun(XMLWriter $xmlWriter, TextRun $textrun): void + { + $xmlWriter->startElement('text:p'); + + $elements = $textrun->getElements(); + foreach ($elements as $element) { + if ($element instanceof Text) { + $xmlWriter->writeRaw($element->getText()); + } + } + + $xmlWriter->endElement(); + } + + /** + * Write table element. + */ + private function writeTable(XMLWriter $xmlWriter, Table $table): void + { + $xmlWriter->startElement('table:table'); + $xmlWriter->writeAttribute('table:name', 'Table' . $table->getElementId()); + + $rows = $table->getRows(); + foreach ($rows as $row) { + $xmlWriter->startElement('table:table-row'); + + $cells = $row->getCells(); + foreach ($cells as $cell) { + $xmlWriter->startElement('table:table-cell'); + + $elements = $cell->getElements(); + $this->writeElements($xmlWriter, $elements); + + $xmlWriter->endElement(); // table:table-cell + } + + $xmlWriter->endElement(); // table:table-row + } + + $xmlWriter->endElement(); // table:table + } +} diff --git a/src/PhpWord/Writer/WPS/Part/Manifest.php b/src/PhpWord/Writer/WPS/Part/Manifest.php new file mode 100644 index 0000000000..02164fbe4e --- /dev/null +++ b/src/PhpWord/Writer/WPS/Part/Manifest.php @@ -0,0 +1,121 @@ +getXmlWriter(); + + $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); + $xmlWriter->startElement('manifest:manifest'); + + // Write namespaces + $xmlWriter->writeAttribute('xmlns:manifest', 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0'); + + // Basic document entries + $this->writeManifestItem($xmlWriter, '/', 'application/vnd.wps-office.document'); + $this->writeManifestItem($xmlWriter, 'content.xml', 'text/xml'); + $this->writeManifestItem($xmlWriter, 'meta.xml', 'text/xml'); + + // Media files + $this->writeMediaFiles($xmlWriter); + + $xmlWriter->endElement(); // manifest:manifest + + return $xmlWriter->getData(); + } + + /** + * Write manifest item. + */ + private function writeManifestItem(XMLWriter $xmlWriter, string $href, string $mediaType): void + { + $xmlWriter->startElement('manifest:file-entry'); + $xmlWriter->writeAttribute('manifest:media-type', $mediaType); + $xmlWriter->writeAttribute('manifest:full-path', $href); + $xmlWriter->endElement(); + } + + /** + * Write media files. + */ + private function writeMediaFiles(XMLWriter $xmlWriter): void + { + $mediaParts = ['section', 'header', 'footer']; + $writtenTargets = []; // Keep track of written targets to avoid duplicates + + foreach ($mediaParts as $partName) { + $media = Media::getElements($partName); + if (!empty($media)) { + foreach ($media as $medium) { + $targetPath = 'Pictures/' . $medium['target']; + // Only write entry if it hasn't been written yet + if (!isset($writtenTargets[$targetPath]) && $medium['type'] == 'image') { + $this->writeManifestItem( + $xmlWriter, + $targetPath, + $this->getMediaType($medium['target']) + ); + $writtenTargets[$targetPath] = true; // Mark as written + } + } + } + } + } + + /** + * Get media type from file extension. + */ + private function getMediaType(string $filename): string + { + $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); + + switch ($extension) { + case 'jpeg': + case 'jpg': + return 'image/jpeg'; + case 'png': + return 'image/png'; + case 'gif': + return 'image/gif'; + case 'bmp': + return 'image/bmp'; + case 'tiff': + case 'tif': + return 'image/tiff'; + case 'svg': + return 'image/svg+xml'; + default: + return 'application/octet-stream'; + } + } +} diff --git a/src/PhpWord/Writer/WPS/Part/Meta.php b/src/PhpWord/Writer/WPS/Part/Meta.php new file mode 100644 index 0000000000..dd5801cffd --- /dev/null +++ b/src/PhpWord/Writer/WPS/Part/Meta.php @@ -0,0 +1,125 @@ +getXmlWriter(); + $phpWord = $this->getParentWriter()->getPhpWord(); + $docInfo = $phpWord->getDocInfo(); + + $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); + $xmlWriter->startElement('office:document-meta'); + + $xmlWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'); + $xmlWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); + $xmlWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/'); + $xmlWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'); + $xmlWriter->writeAttribute('xmlns:wps', 'http://wps.kdanmobile.com/2017/office'); + + $xmlWriter->startElement('office:meta'); + + // Creator + $creator = $docInfo->getCreator(); + if ($creator !== null) { + $xmlWriter->writeElement('meta:initial-creator', $creator); + $xmlWriter->writeElement('dc:creator', $creator); + } + + // Creation date + $createdDate = $docInfo->getCreated(); + if ($createdDate !== null) { + $xmlWriter->startElement('meta:creation-date'); + $xmlWriter->writeRaw((string) $createdDate); + $xmlWriter->endElement(); + } + + // Modification date + $modifiedDate = $docInfo->getModified(); + if ($modifiedDate !== null) { + $xmlWriter->startElement('dc:date'); + $xmlWriter->writeRaw((string) $modifiedDate); + $xmlWriter->endElement(); + } + + // Title + $title = $docInfo->getTitle(); + if ($title !== null) { + $xmlWriter->writeElement('dc:title', $title); + } + + // Description + $description = $docInfo->getDescription(); + if ($description !== null) { + $xmlWriter->writeElement('dc:description', $description); + } + + // Subject + $subject = $docInfo->getSubject(); + if ($subject !== null) { + $xmlWriter->writeElement('dc:subject', $subject); + } + + // Keywords + $keywords = $docInfo->getKeywords(); + if ($keywords !== null) { + $xmlWriter->writeElement('meta:keyword', $keywords); + } + + // Category + $category = $docInfo->getCategory(); + if ($category !== null) { + $this->writeUserDefined($xmlWriter, 'Category', $category); + } + + // Company + $company = $docInfo->getCompany(); + if ($company !== null) { + $this->writeUserDefined($xmlWriter, 'Company', $company); + } + + $xmlWriter->endElement(); // office:meta + $xmlWriter->endElement(); // office:document-meta + + return $xmlWriter->getData(); + } + + /** + * Write user defined value. + */ + private function writeUserDefined(XMLWriter $xmlWriter, string $name, string $value): void + { + $xmlWriter->startElement('meta:user-defined'); + $xmlWriter->writeAttribute('meta:name', $name); + $xmlWriter->writeRaw($value); + $xmlWriter->endElement(); + } +} diff --git a/tests/PhpWordTests/Writer/WPS/MediaTest.php b/tests/PhpWordTests/Writer/WPS/MediaTest.php new file mode 100644 index 0000000000..5c12f44a7c --- /dev/null +++ b/tests/PhpWordTests/Writer/WPS/MediaTest.php @@ -0,0 +1,109 @@ +addSection(); // Add a section to avoid errors during write + $writer = new WPS($phpWord); + /** @var Content $content */ + $content = $writer->getWriterPart('content'); // Get part from writer + + // Act: Call the write method + $result = $content->write(); + + // Assert that the result is a string + self::assertIsString($result); + // Assert that the result contains expected XML structure + self::assertStringContainsString('', $result); + self::assertStringContainsString('', $result); + self::assertStringContainsString('', $result); + self::assertStringContainsString('', $result); + self::assertStringContainsString('', $result); + } +} diff --git a/tests/PhpWordTests/Writer/WPS/Part/ManifestTest.php b/tests/PhpWordTests/Writer/WPS/Part/ManifestTest.php new file mode 100644 index 0000000000..1103a71ef8 --- /dev/null +++ b/tests/PhpWordTests/Writer/WPS/Part/ManifestTest.php @@ -0,0 +1,25 @@ +write(); + + // Assert that the result is a string + self::assertIsString($result); + + // Assert that the result contains expected XML structure + self::assertStringContainsString('getWriterPart('meta'); // Get part from writer + + // Act: Call the write method + $result = $meta->write(); + + // Assert that the result is a string + self::assertIsString($result); + // Assert that the result contains expected XML structure + self::assertStringContainsString('', $result); + } +} diff --git a/tests/PhpWordTests/Writer/WPSTest.php b/tests/PhpWordTests/Writer/WPSTest.php new file mode 100644 index 0000000000..8c4b45044a --- /dev/null +++ b/tests/PhpWordTests/Writer/WPSTest.php @@ -0,0 +1,116 @@ +addSection(); + $section->addText('Hello, WPS!'); + + $writer = new WPS($phpWord); + $tempFile = tempnam(sys_get_temp_dir(), 'wps'); + $writer->save($tempFile); + + self::assertFileExists($tempFile); + + // Test ZIP archive content + $zip = new ZipArchive(); + $zip->open($tempFile); + + // Verify required files exist + self::assertTrue($zip->locateName('content.xml') !== false); + self::assertTrue($zip->locateName('meta.xml') !== false); + self::assertTrue($zip->locateName('META-INF/manifest.xml') !== false); + + $zip->close(); + + $content = file_get_contents($tempFile); + if (is_string($content)) { + self::assertEquals('PK', substr($content, 0, 2)); + } + + unlink($tempFile); + } + + public function testWriterParts(): void + { + $phpWord = new PhpWord(); + $writer = new WPS($phpWord); + + // Test the writer parts are initialized correctly + self::assertInstanceOf('PhpOffice\\PhpWord\\Writer\\WPS\\Part\\Content', $writer->getWriterPart('content')); + self::assertInstanceOf('PhpOffice\\PhpWord\\Writer\\WPS\\Part\\Meta', $writer->getWriterPart('meta')); + self::assertInstanceOf('PhpOffice\\PhpWord\\Writer\\WPS\\Part\\Manifest', $writer->getWriterPart('manifest')); + } + + public function testWithMedia(): void + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + // Add an image to the document + $imagePath = realpath('tests/PhpWordTests/_files/images/earth.jpg'); + self::assertIsString($imagePath, 'Test image file not found or accessible at the expected location.'); // Ensure path is valid string + self::assertFileExists($imagePath, "Test image file not found at: {$imagePath}"); // Use validated path + $section->addImage($imagePath); // Use validated path + + // Create header and add an image to it + $header = $section->addHeader(); + $header->addImage($imagePath); // Use validated path + + // Create footer and add an image to it + $footer = $section->addFooter(); + $footer->addImage($imagePath); // Use validated path + + $writer = new WPS($phpWord); + $tempFile = tempnam(sys_get_temp_dir(), 'wps_media_'); // Use a distinct prefix + if ($tempFile === false) { + self::fail('Failed to create temporary file.'); + } + $writer->save($tempFile); + + // Test ZIP archive contains images + $zip = new ZipArchive(); + self::assertTrue($zip->open($tempFile) === true, "Failed to open generated ZIP file: {$tempFile}"); + + // Verify the Pictures directory exists and contains images + self::assertTrue($zip->locateName('Pictures/') !== false, "'Pictures/' directory not found in ZIP."); + self::assertTrue($zip->locateName('Pictures/image1.jpg') !== false, "'Pictures/image1.jpg' not found in ZIP."); + + $zip->close(); + unlink($tempFile); + } + + public function testSaveToOutput(): void + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $section->addText('Hello, WPS!'); + + $writer = new WPS($phpWord); + + ob_start(); + $writer->save('php://output'); + $content = ob_get_clean(); + + // Check that the output starts with the ZIP file signature (PK header) + if (is_string($content)) { + self::assertEquals('PK', substr($content, 0, 2)); + } + } +} diff --git a/tests/PhpWordTests/Writer/Word2007/Element/TOCTest.php b/tests/PhpWordTests/Writer/Word2007/Element/TOCTest.php index 95e79114aa..d250382370 100644 --- a/tests/PhpWordTests/Writer/Word2007/Element/TOCTest.php +++ b/tests/PhpWordTests/Writer/Word2007/Element/TOCTest.php @@ -66,8 +66,14 @@ public function testWriteTitleWithoutpageNumber(): void //more than one title and random text for create more than one page for ($i = 1; $i <= 10; ++$i) { $section->addTitle('Title ' . $i, 1); - $content = file_get_contents('https://loripsum.net/api/10/long'); - \PhpOffice\PhpWord\Shared\Html::addHtml($section, $content ? $content : '', false, false); + // Using static content instead of making a network request + $content = '

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. + Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. + Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, + euismod non, mi.

Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, + non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, + consequat in, pretium a, enim.

'; + \PhpOffice\PhpWord\Shared\Html::addHtml($section, $content, false, false); $section->addPageBreak(); }