Setting up automatic watermarking in 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1212
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    815
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    565
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    980

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

  • OnBeforeFileAdd event handler with $moduleId filtering
  • List of excluded infoblocks in b_option
  • GD error handling without upload blocking
  • For bulk import: async processing via agent
  • Monitor /bitrix/watermark.log for corrupted images