Configuration of Automatic Watermark Application in 1C-Bitrix
Manager uploads a new product image via admin panel — and it should automatically get a watermark without any additional action. Manual processing after upload is a broken process: someone will forget, someone will do it wrong. Need interception at file upload event level.
Interception points during image uploads
In Bitrix, files are uploaded via CFile::SaveFile() and CFile::Add(). The OnBeforeFileAdd event allows intercepting upload before saving to disk:
AddEventHandler('main', 'OnBeforeFileAdd', function(&$fileFields, $moduleId) {
// $moduleId: 'iblock', 'sale', 'catalog', etc.
if ($moduleId !== 'iblock') {
return; // Process only catalog images
}
$mimeType = $fileFields['type'] ?? '';
if (!str_starts_with($mimeType, 'image/')) {
return;
}
$tmpFile = $fileFields['tmp_name'];
applyWatermarkInPlace($tmpFile);
});
Function applyWatermarkInPlace modifies the temporary file before saving to /upload/. This means: database and disk will contain already processed file, without need to store original and cache separately.
Difference between update and new upload
Event OnBeforeFileAdd only fires on new uploads. When updating catalog element, if image doesn't change, file isn't rewritten — event won't fire. This is correct behavior.
Problem: if admin uploads image via bulk CSV import or via REST API (iblock.element.update), event OnBeforeFileAdd should also fire — and it does, because import ultimately calls CFile::SaveFile().
Exceptions from automatic watermarking
Not all images need marking. Company logo in header, category icons, system images — all pass through CFile. Filtering by $moduleId solves only part of the problem.
For more precise control, add context check via session variable:
AddEventHandler('main', 'OnBeforeFileAdd', function(&$fileFields, $moduleId) {
if ($moduleId !== 'iblock') return;
if (!isset($_SESSION['WATERMARK_ENABLED'])) return;
$iblockId = $_SESSION['CURRENT_IBLOCK_ID'] ?? null;
$noWatermarkIblocks = \Bitrix\Main\Config\Option::get('catalog', 'no_watermark_iblocks', '');
$excluded = array_map('intval', explode(',', $noWatermarkIblocks));
if ($iblockId && in_array($iblockId, $excluded)) return;
applyWatermarkInPlace($fileFields['tmp_name']);
});
Variable CURRENT_IBLOCK_ID is set in handler of event OnBeforeIBlockElementAdd / OnBeforeIBlockElementUpdate. List of excluded infoblocks is stored in b_option.
Error handling during watermarking
If GD processing fails (corrupted file, unsupported format), upload shouldn't be blocked. Wrapper applyWatermarkInPlace in try/catch block — on error file is saved without watermark, error is written to log:
function applyWatermarkInPlace(string $tmpFile): void
{
try {
// GD processing
$result = processWatermark($tmpFile);
if ($result) {
file_put_contents($tmpFile, $result);
}
} catch (\Throwable $e) {
\Bitrix\Main\Diag\Debug::writeToFile(
$e->getMessage(), 'watermark_error', '/bitrix/watermark.log'
);
// Don't interrupt upload — file will be saved as-is
}
}
Performance during bulk upload
During catalog import of 5,000 products with 3 images each — 15,000 GD operations. On average server, one image processing takes 50–150 ms. Total: 12–37 minutes added to import time. If unacceptable — switch to async scheme: save original, queue task in b_agent, agent processes in batches of 100 images.
What to configure
-
OnBeforeFileAddevent handler with$moduleIdfiltering - List of excluded infoblocks in
b_option - GD error handling without upload blocking
- For bulk import: async processing via agent
- Monitor
/bitrix/watermark.logfor corrupted images







