{"record":{"id":"9d4a23a0c9d356f5","repo":"Intervention/image","slug":"unable-to-read-jfif-header","errorCode":null,"errorMessage":"Unable to read JFIF header","messagePattern":"Unable to read JFIF header","errorType":"exception","errorClass":"AnalyzerException","httpStatus":null,"severity":"info","filePath":"src/Drivers/Gd/Analyzers/ResolutionAnalyzer.php","lineNumber":115,"sourceCode":"            fclose($handle);\n        }\n    }\n\n    /**\n     * @param resource $handle\n     * @throws AnalyzerException\n     * @return array<float>\n     */\n    private function resolutionFromJfifHeader($handle): array\n    {\n        // read first 20 bytes\n        rewind($handle);\n        $header = fread($handle, 20);\n\n        // find the JFIF segment\n        $offset = strpos($header, 'JFIF');\n        if ($offset === false) {\n            throw new AnalyzerException('Unable to read JFIF header');\n        }\n\n        // read bytes at known offsets relative to JFIF\n        $units = ord($header[$offset + 7]);\n        $x = unpack('n', substr($header, $offset + 8, 2))[1];\n        $y = unpack('n', substr($header, $offset + 10, 2))[1];\n\n        if ($units === 2) { // unit is dots per cm → convert to DPI\n            return [round($x * 2.54), round($y * 2.54)];\n        }\n\n        return [$x, $y]; // unit is DPI or no unit\n    }\n\n    /**\n     * @param resource $handle\n     * @throws MissingDependencyException\n     * @throws AnalyzerException","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/Intervention/image/blob/5598b9e39751c34afc5cdee84abef77f92c26f68/src/Drivers/Gd/Analyzers/ResolutionAnalyzer.php#L97-L133","documentation":"The first recovery strategy for JPEG resolution reads the first 20 bytes of the origin file and looks for the literal 'JFIF' segment marker. If strpos() cannot find it, this AnalyzerException is thrown and the caller falls through to the EXIF parser. It is an internal stepping stone: readResolutionFromOrigin() catches it, and analyze() ultimately swallows everything, so it never surfaces through the public API.","triggerScenarios":"JPEG files whose first segment is APP1/EXIF (camera files) or APP14/Adobe instead of APP0/JFIF; files that are not JPEG at all; truncated headers shorter than the JFIF offset.","commonSituations":"Camera-origin JPEGs (EXIF first), Photoshop-exported Adobe JPEGs, or renamed non-JPEG files being inspected for resolution.","solutions":["Nothing to fix from user land; EXIF and pHYs parsers run next automatically","If you need JFIF density honored, re-encode with a JFIF header (imagemagick 'convert -density 96')","For debugging, inspect the first bytes yourself: bin2hex(substr(file_get_contents($path), 0, 20))"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Internal step only; EXIF and pHYs parsers follow automatically","Re-encode JPEGs with a JFIF header when JFIF density is required","Do not build logic that depends on which internal recovery branch failed"],"tags":["gd","resolution","jpeg","jfif","internal"],"backgroundTag":"missing-jfif-header","analyzedSha":"5598b9e39751c34afc5cdee84abef77f92c26f68","analyzedAt":"2026-08-23T02:17:31.068Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}