*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/**
* \file core/substitutions/functions_epcqr.lib.php
* \ingroup epcqr
* \brief Substitutionsfunktionen für QR-Codes und Bilder in Dokumenten
*/
/**
* Füllt Substitutionsarray mit Bildpfaden und QR-Code-Daten
*
* @param array &$substitutionarray Array mit Substitutionswerten
* @param Translate $langs Sprachobjekt
* @param CommonObject $object Dolibarr-Objekt (Rechnung, Angebot, etc.)
* @param string $outputlangs Ausgabesprache
* @return void
*/
function epcqr_completesubstitutionarray(&$substitutionarray, $langs, $object, $outputlangs = null)
{
global $conf, $db;
dol_syslog("EPCQR: START completesubstitutionarray für ".get_class($object), LOG_DEBUG);
// Prüfen ob Objekt gültig ist
if (!is_object($object) || empty($object->id)) {
dol_syslog("EPCQR: Object ist null oder hat keine ID - überspringe", LOG_DEBUG);
return;
}
// QR-Code Pfad aus Extra-Feldern
$qrCodePath = '';
// Prüfen ob Extra-Felder vorhanden sind
if (isset($object->array_options['options_qrcodepath'])) {
$qrCodePath = $object->array_options['options_qrcodepath'];
dol_syslog("EPCQR: QR-Code Pfad gefunden: ".$qrCodePath, LOG_DEBUG);
}
// Substitution für QR-Code-Bildpfad
$substitutionarray['qrcode_path'] = $qrCodePath;
// Für ODT-Dokumente: Spezielle Marker für Bildoperationen
// Diese werden später von der ODT-Erweiterung verarbeitet
$substitutionarray['__IMAGE_qrcode__'] = $qrCodePath;
// Allgemeine Bildfunktion: Jedes Extrafeld das auf "_imagepath" endet
// wird als Bildpfad interpretiert
if (!empty($object->array_options)) {
foreach ($object->array_options as $key => $value) {
if (preg_match('/^options_(.+)_imagepath$/', $key, $matches)) {
$fieldname = $matches[1];
$substitutionarray['__IMAGE_'.$fieldname.'__'] = $value;
dol_syslog("EPCQR: Bildpfad gefunden für ".$fieldname.": ".$value, LOG_DEBUG);
}
}
}
dol_syslog("EPCQR: ENDE completesubstitutionarray", LOG_DEBUG);
}
/**
* Fügt Bilder in ODT-Dokumente ein
*
* Diese Funktion wird nach der ODT-Generierung aufgerufen und
* ersetzt Bild-Marker durch tatsächliche Bilder im ODT
*
* @param string $odfFilePath Pfad zur generierten ODT-Datei
* @param array $imageData Array mit Bildinformationen
* @return bool true bei Erfolg, false bei Fehler
*/
function epcqr_insertImagesIntoODT($odfFilePath, $imageData)
{
global $conf;
dol_syslog("EPCQR: START insertImagesIntoODT für ".$odfFilePath, LOG_DEBUG);
if (!file_exists($odfFilePath)) {
dol_syslog("EPCQR: ODT-Datei existiert nicht: ".$odfFilePath, LOG_ERR);
return false;
}
// Temporäres Verzeichnis erstellen
$tmpDir = sys_get_temp_dir() . '/odt_epcqr_' . uniqid();
if (!mkdir($tmpDir)) {
dol_syslog("EPCQR: Konnte temporäres Verzeichnis nicht erstellen", LOG_ERR);
return false;
}
try {
// 1. ODT entpacken (ODT ist ZIP)
$zip = new ZipArchive();
if ($zip->open($odfFilePath) !== true) {
dol_syslog("EPCQR: Konnte ODT nicht öffnen: ".$odfFilePath, LOG_ERR);
epcqr_deleteDir($tmpDir);
return false;
}
$zip->extractTo($tmpDir);
$zip->close();
// 2. Pictures-Verzeichnis erstellen falls nicht vorhanden
if (!is_dir($tmpDir.'/Pictures')) {
mkdir($tmpDir.'/Pictures');
}
// 3. Bilder kopieren und XML vorbereiten
$manifest = file_get_contents($tmpDir.'/META-INF/manifest.xml');
$content = file_get_contents($tmpDir.'/content.xml');
$imageReplacements = array();
foreach ($imageData as $marker => $imagePath) {
if (empty($imagePath) || !file_exists($imagePath)) {
dol_syslog("EPCQR: Bild existiert nicht: ".$imagePath." für Marker ".$marker, LOG_WARNING);
continue;
}
// Bildinfo ermitteln
$imageInfo = @getimagesize($imagePath);
if ($imageInfo === false) {
dol_syslog("EPCQR: Konnte Bildinfo nicht lesen: ".$imagePath, LOG_WARNING);
continue;
}
$extension = strtolower(pathinfo($imagePath, PATHINFO_EXTENSION));
$mimeType = $imageInfo['mime'];
$imageWidth = $imageInfo[0];
$imageHeight = $imageInfo[1];
// Eindeutigen Bildnamen generieren
$imageFilename = 'img_'.md5($marker).'.'.$extension;
$imageDest = $tmpDir.'/Pictures/'.$imageFilename;
// Bild kopieren
if (!copy($imagePath, $imageDest)) {
dol_syslog("EPCQR: Konnte Bild nicht kopieren: ".$imagePath, LOG_ERR);
continue;
}
// Manifest.xml aktualisieren
if (strpos($manifest, 'Pictures/'.$imageFilename) === false) {
$newEntry = '';
$manifest = str_replace('', $newEntry."\n", $manifest);
}
// Bildgröße in cm berechnen (Annahme: 96 DPI)
$widthCm = round(($imageWidth / 96) * 2.54, 2);
$heightCm = round(($imageHeight / 96) * 2.54, 2);
// Maximale Größe begrenzen
$maxWidth = 6; // cm
$maxHeight = 6; // cm
if ($widthCm > $maxWidth || $heightCm > $maxHeight) {
$ratio = min($maxWidth / $widthCm, $maxHeight / $heightCm);
$widthCm = round($widthCm * $ratio, 2);
$heightCm = round($heightCm * $ratio, 2);
}
// ODF draw:frame XML-Element erstellen
$imageXml = ''
.''
.'';
$imageReplacements['{'.$marker.'}'] = $imageXml;
dol_syslog("EPCQR: Bild vorbereitet: ".$marker." -> ".$imageFilename, LOG_DEBUG);
}
// 4. Marker in content.xml ersetzen
foreach ($imageReplacements as $marker => $xml) {
$content = str_replace($marker, $xml, $content);
}
// 5. Dateien zurückschreiben
file_put_contents($tmpDir.'/META-INF/manifest.xml', $manifest);
file_put_contents($tmpDir.'/content.xml', $content);
// 6. Neue ODT erstellen
$zip = new ZipArchive();
if ($zip->open($odfFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
dol_syslog("EPCQR: Konnte neue ODT nicht erstellen", LOG_ERR);
epcqr_deleteDir($tmpDir);
return false;
}
// mimetype MUSS zuerst und unkomprimiert
$zip->addFile($tmpDir.'/mimetype', 'mimetype');
$zip->setCompressionName('mimetype', ZipArchive::CM_STORE);
// Restliche Dateien hinzufügen
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($tmpDir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file) {
if (!$file->isFile()) {
continue;
}
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($tmpDir) + 1);
// mimetype überspringen (bereits hinzugefügt)
if ($relativePath === 'mimetype') {
continue;
}
// Backslashes zu Forward-Slashes
$relativePath = str_replace('\\', '/', $relativePath);
$zip->addFile($filePath, $relativePath);
}
$zip->close();
// 7. Cleanup
epcqr_deleteDir($tmpDir);
dol_syslog("EPCQR: ENDE insertImagesIntoODT - Erfolgreich", LOG_DEBUG);
return true;
} catch (Exception $e) {
dol_syslog("EPCQR: Exception in insertImagesIntoODT: ".$e->getMessage(), LOG_ERR);
if (is_dir($tmpDir)) {
epcqr_deleteDir($tmpDir);
}
return false;
}
}
/**
* Hilfsfunktion: Löscht Verzeichnis rekursiv
*
* @param string $dir Verzeichnispfad
* @return void
*/
function epcqr_deleteDir($dir)
{
if (!is_dir($dir)) {
return;
}
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $dir.'/'.$item;
if (is_dir($path)) {
epcqr_deleteDir($path);
} else {
unlink($path);
}
}
rmdir($dir);
}