false, 'method' => '', ); if ( ! is_readable( $file ) ) { return $result; } $size = filesize( $file ); if ( false === $size || 0 === $size ) { return $result; } // Read up to the first 512 KB — metadata lives at the top of the file. $chunk = $this->read_chunk( $file, min( $size, 512 * 1024 ) ); if ( '' === $chunk ) { return $result; } // 1. C2PA manifest (JUMBF superbox labelled c2pa). if ( false !== strpos( $chunk, 'c2pa' ) && ( false !== strpos( $chunk, 'jumb' ) || false !== strpos( $chunk, 'jumd' ) ) ) { $result['detected'] = true; $result['method'] = 'c2pa'; return $result; } // 2. IPTC/XMP digital source type. foreach ( self::IPTC_AI_SOURCETYPES as $type ) { if ( false !== stripos( $chunk, $type ) ) { $result['detected'] = true; $result['method'] = 'iptc-digitalsourcetype'; return $result; } } // 3. Known generator software strings (weakest signal). foreach ( self::GENERATOR_STRINGS as $needle ) { if ( false !== stripos( $chunk, $needle ) ) { $result['detected'] = true; $result['method'] = 'generator-string'; return $result; } } return $result; } /** * Read the first N bytes of a file. * * @param string $file File path. * @param int $bytes Number of bytes. * @return string */ private function read_chunk( $file, $bytes ) { $handle = fopen( $file, 'rb' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen if ( ! $handle ) { return ''; } $data = fread( $handle, $bytes ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose return false === $data ? '' : $data; } }