144 lines
4.8 KiB
PHP
144 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
if ($argc !== 2) {
|
|
fwrite(STDERR, "Usage: php adjust_docx_whitespace.php <document.docx>\n");
|
|
exit(2);
|
|
}
|
|
|
|
$path = $argv[1];
|
|
$wordNs = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
|
|
$zip = new ZipArchive();
|
|
if ($zip->open($path) !== true) {
|
|
throw new RuntimeException("Cannot open DOCX: {$path}");
|
|
}
|
|
|
|
$documentXml = $zip->getFromName('word/document.xml');
|
|
if ($documentXml === false) {
|
|
throw new RuntimeException('word/document.xml is missing');
|
|
}
|
|
|
|
$doc = new DOMDocument('1.0', 'UTF-8');
|
|
$doc->preserveWhiteSpace = false;
|
|
$doc->formatOutput = false;
|
|
if (!$doc->loadXML($documentXml)) {
|
|
throw new RuntimeException('word/document.xml is invalid');
|
|
}
|
|
|
|
$xpath = new DOMXPath($doc);
|
|
$xpath->registerNamespace('w', $wordNs);
|
|
|
|
function attr(DOMElement $element, string $name, string $value, string $ns): void
|
|
{
|
|
$element->setAttributeNS($ns, 'w:' . $name, $value);
|
|
}
|
|
|
|
function child(DOMDocument $doc, DOMElement $parent, string $localName, string $ns): DOMElement
|
|
{
|
|
foreach ($parent->childNodes as $node) {
|
|
if ($node instanceof DOMElement && $node->namespaceURI === $ns && $node->localName === $localName) {
|
|
return $node;
|
|
}
|
|
}
|
|
$element = $doc->createElementNS($ns, 'w:' . $localName);
|
|
$parent->appendChild($element);
|
|
return $element;
|
|
}
|
|
|
|
foreach ($xpath->query('//w:body/w:p | //w:body/w:tbl//w:p') as $paragraph) {
|
|
if (!$paragraph instanceof DOMElement) {
|
|
continue;
|
|
}
|
|
|
|
$text = '';
|
|
foreach ($xpath->query('.//w:t', $paragraph) as $textNode) {
|
|
$text .= $textNode->textContent;
|
|
}
|
|
$text = trim(preg_replace('/\s+/u', ' ', $text) ?? '');
|
|
|
|
$pPrNode = $xpath->query('./w:pPr', $paragraph)->item(0);
|
|
if (!$pPrNode instanceof DOMElement) {
|
|
$pPrNode = $doc->createElementNS($wordNs, 'w:pPr');
|
|
$paragraph->insertBefore($pPrNode, $paragraph->firstChild);
|
|
}
|
|
$spacing = child($doc, $pPrNode, 'spacing', $wordNs);
|
|
|
|
$before = '0';
|
|
$after = '30';
|
|
$line = '320';
|
|
|
|
if ($text === '') {
|
|
$after = '20';
|
|
$line = '120';
|
|
} elseif (preg_match('/^[1-7]\.\s/u', $text)) {
|
|
$before = '120';
|
|
$after = '45';
|
|
$line = '330';
|
|
} elseif (preg_match('/^[1-7]\.\d\s/u', $text)) {
|
|
$before = '70';
|
|
$after = '30';
|
|
$line = '320';
|
|
} elseif (str_starts_with($text, '•')) {
|
|
$after = '15';
|
|
$line = '310';
|
|
} elseif (str_starts_with($text, 'วันที่ 5 มกราคม')) {
|
|
$before = '40';
|
|
$after = '100';
|
|
$line = '330';
|
|
} elseif (str_starts_with($text, 'เรื่อง') || str_starts_with($text, 'เรียน')) {
|
|
$after = '55';
|
|
$line = '330';
|
|
} elseif (str_starts_with($text, 'ลงชื่อ')) {
|
|
$before = '160';
|
|
$after = '10';
|
|
$line = '310';
|
|
} elseif (str_starts_with($text, '(') || str_starts_with($text, 'ตำแหน่ง') || str_starts_with($text, 'วันที่ _') || str_starts_with($text, 'ผู้มีอำนาจ')) {
|
|
$after = '10';
|
|
$line = '310';
|
|
} elseif (str_starts_with($text, 'บริษัท บี.อาร์.เอ็น.') || str_starts_with($text, 'เอกสารฉบับนี้') || str_starts_with($text, 'ผลส่งมอบประกอบ') || str_starts_with($text, 'การเปลี่ยนแปลง') || str_starts_with($text, 'จึงจัดทำ')) {
|
|
$after = '55';
|
|
$line = '330';
|
|
}
|
|
|
|
attr($spacing, 'before', $before, $wordNs);
|
|
attr($spacing, 'after', $after, $wordNs);
|
|
attr($spacing, 'line', $line, $wordNs);
|
|
attr($spacing, 'lineRule', 'auto', $wordNs);
|
|
}
|
|
|
|
foreach ($xpath->query('//w:sectPr/w:pgMar') as $margin) {
|
|
if (!$margin instanceof DOMElement) {
|
|
continue;
|
|
}
|
|
attr($margin, 'top', '850', $wordNs);
|
|
attr($margin, 'bottom', '850', $wordNs);
|
|
attr($margin, 'left', '1080', $wordNs);
|
|
attr($margin, 'right', '1080', $wordNs);
|
|
attr($margin, 'header', '400', $wordNs);
|
|
attr($margin, 'footer', '400', $wordNs);
|
|
}
|
|
|
|
foreach ($xpath->query('//w:tbl/w:tblPr') as $tableProperties) {
|
|
if (!$tableProperties instanceof DOMElement) {
|
|
continue;
|
|
}
|
|
$margins = child($doc, $tableProperties, 'tblCellMar', $wordNs);
|
|
foreach (['top' => '45', 'left' => '70', 'bottom' => '45', 'right' => '70'] as $side => $width) {
|
|
$edge = child($doc, $margins, $side, $wordNs);
|
|
attr($edge, 'w', $width, $wordNs);
|
|
attr($edge, 'type', 'dxa', $wordNs);
|
|
}
|
|
}
|
|
|
|
$updatedXml = $doc->saveXML();
|
|
if ($updatedXml === false) {
|
|
throw new RuntimeException('Cannot serialize adjusted document XML');
|
|
}
|
|
if (!$zip->addFromString('word/document.xml', $updatedXml)) {
|
|
throw new RuntimeException('Cannot update word/document.xml');
|
|
}
|
|
$zip->close();
|
|
|
|
echo $path . PHP_EOL;
|