PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Writer\Exception
$bitmap doesn't appear to be a valid bitmap image.\n
Error message
$bitmap doesn't appear to be a valid bitmap image.\n
What it means
BMP files must begin with the 2-byte ASCII signature 'BM'. processBitmap() unpacks the first two bytes and throws when they differ, so PNG/JPEG/GIF/WEBP content is rejected even if the filename ends in .bmp. The file was opened and is large enough - it is simply not a bitmap.
Source
Thrown at src/PhpSpreadsheet/Writer/Xls/Worksheet.php:2480
// Open file.
$bmp_fd = @fopen($bitmap, 'rb');
if ($bmp_fd === false || 0 === (int) filesize($bitmap)) {
throw new WriterException("Couldn't import $bitmap");
}
// Slurp the file into a string.
$data = (string) fread($bmp_fd, (int) filesize($bitmap));
// Check that the file is big enough to be a bitmap.
if (strlen($data) <= 0x36) {
throw new WriterException("$bitmap doesn't contain enough data.\n");
}
// The first 2 bytes are used to identify the bitmap.
$identity = unpack('A2ident', $data);
if ($identity === false || $identity['ident'] != 'BM') {
throw new WriterException("$bitmap doesn't appear to be a valid bitmap image.\n");
}
// Remove bitmap data: ID.
$data = substr($data, 2);
// Read and remove the bitmap size. This is more reliable than reading
// the data size at offset 0x22.
//
$size_array = unpack('Vsa', substr($data, 0, 4)) ?: [];
/** @var int */
$size = $size_array['sa'];
$data = substr($data, 4);
$size -= 0x36; // Subtract size of bitmap header.
$size += 0x0C; // Add size of BIFF header.
// Remove bitmap data: reserved, offset, header length.
$data = substr($data, 12);
View on GitHub (pinned to 65b080eef4)
Solutions
- Convert the image to an actual BMP (e.g. imagebmp() from GD) before inserting
- Detect the real format with getimagesize() and route accordingly
- Use the Drawing API with PNG/JPEG, which handles modern formats natively
Example fix
// before
$worksheetWriter->insertBitmap(1, 1, 'photo.png.bmphack'); // PNG data
// after
$im = imagecreatefrompng('photo.png');
imagebmp($im, 'photo.bmp');
$worksheetWriter->insertBitmap(1, 1, 'photo.bmp'); Defensive patterns
Strategy: validation
Validate before calling
function isWindowsBmp(string $path): bool
{
$fh = fopen($path, 'rb');
if ($fh === false) {
return false;
}
$magic = fread($fh, 2);
fclose($fh);
return $magic === 'BM';
}
if (!isWindowsBmp($path)) {
$info = getimagesize($path) ?: [];
throw new InvalidArgumentException(
'Not a BMP (detected ' . ($info[2] ?? 'unknown') . '); convert before insertBitmap()'
);
}
$worksheetWriter->insertBitmap($row, $col, $path); Try / catch
try {
$worksheetWriter->insertBitmap($row, $col, $path);
} catch (\PhpOffice\PhpSpreadsheet\Writer\Exception $e) {
if (str_contains($e->getMessage(), 'valid bitmap')) {
// route through GD to convert whatever it is into a 24-bit BMP, then retry once
}
} Prevention
- Never trust extensions: sniff magic bytes or use getimagesize() before embedding
- Standardize on PNG/JPEG + Drawing API; only fall back to BMP for legacy .xls requirements
When it happens
Trigger: Passing a PNG or JPEG renamed to .bmp to insertBitmap(); passing a data-stream or non-image file that survived the size check.
Common situations: Assuming the file extension equals the real format; pipelines that 'convert' images by renaming; files served with the wrong content by an upstream system.
Related errors
- Couldn't import $bitmap
- $bitmap doesn't contain enough data.\n
- $bitmap: largest image width supported is 65k.\n
- $bitmap: largest image height supported is 65k.\n
- $bitmap isn't a 24bit true color bitmap.\n
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/89892b5cde478740.
Report an issue: GitHub.