1. Go to this page and download the library: Download belisoful/php-image library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
belisoful / php-image example snippets
$jpeg = JPEGImage::fromStream($psr7Stream); // any PSR-7 implementation
$jpeg = JPEGImage::fromStream(fopen('in.jpg', 'rb')); // or a raw resource
$jpeg->getEXIF()?->setValueByName('Artist', 'A. Photographer');
$jpeg->writeTo(fopen('out.jpg', 'wb')); // resource out
$jpeg->writeTo($psr7Stream); // or a stream out
$handle = $jpeg->toStream(); // a rewound PHP stream resource
$exif = EXIF::scanFile('500MB-scan.tif'); // seeks; never loads the pixels
$exif->getMake();
$exif->getThumbnail();
use Belisoful\Image\JPEGImage;
use Belisoful\Image\PNGImage;
use Belisoful\Image\WebPImage;
$jpeg = JPEGImage::fromFile('photo.jpg'); // or fromString() / fromStream()
[$w, $h] = [$jpeg->getWidth(), $jpeg->getHeight()];
$png = PNGImage::fromFile('image.png');
$icc = $png->getICCProfile(); // null when absent
$webp = WebPImage::fromFile('image.webp'); // VP8 / VP8L / VP8X all supported
use Belisoful\Image\JPEGImage;
use Belisoful\Image\Meta\IPTC;
use Belisoful\Image\Meta\IPTCTags;
$jpeg = JPEGImage::fromFile('photo.jpg');
$iptc = $jpeg->getIPTC() ?? new IPTC();
$iptc[IPTCTags::CaptionAbstract] = 'Edited caption';
$iptc['Keywords'] = ['php', 'image', 'metadata']; // repeatable dataset
$jpeg->setIPTC($iptc);
$jpeg->save('photo-out.jpg'); // image data unchanged, metadata rewritten
$jfif = $jpeg->getJFIF();
[$jfif->getXDensity(), $jfif->getYDensity(), $jfif->getUnits()];
$jfif->setImage($gdThumbnail); // embed an RGB thumbnail (<= 255x255)
// JFXX is the APP0 thumbnail extension, with JPEG, palette, or RGB encodings.
$jfxx = $jpeg->getJFXX() ?? new JFXX();
$jfxx->setImage($gdThumbnail, JFXX::JPEG_THUMB); // or PALETTE_THUMB / COLOR_THUMB / EFFICIENCY_THUMB
$thumb = $jfxx->getImage(); // back to a \GdImage or \Imagick
$jpeg->setJFXX($jfxx);
use Belisoful\Image\JPEGImage;
$jpeg = JPEGImage::fromFile('photo.jpg');
$exif = $jpeg->getEXIF();
$exif->getMake(); // 'Canon'
$exif->getValueByName('FNumber'); // [[71, 10]]
$exif->getTextByName('FNumber'); // '7.1'
$exif->getTextByName('ExposureTime'); // '1/125 seconds'
$exif->getTextByName('GPSLatitude'); // '34° 3' 30"'
$thumb = $exif->getThumbnail(); // IFD1 JPEG bytes
$note = $exif->getMakernote(); // detected via the maker registry
$note?->getValues(); // name => interpreted text
$note?->getThumbnail(); // Casio/Olympus/Minolta embedded thumbnail
$exif->setValueByName('Artist', 'A. Photographer');
$jpeg->save('photo-out.jpg'); // rewritten; makernote pinned at its offset
$exif->getLatitude(); // -33.86882 (S negative, W negative)
$exif->setLatitude(60.392990);
$exif->setLongitude(5.324383);
$exif->setAltitude(-42.25); // below sea level
$exif->setGpsTimestamp(new DateTimeImmutable('now')); // stored as UTC
use Belisoful\Image\PrivacyCategory;
// One call on the container reaches every metadata block it holds.
$jpeg = JPEGImage::fromFile('photo.jpg');
$removed = $jpeg->clearPrivateData(); // everything: the safe default
$jpeg->save('photo-shareable.jpg');
$png->clearPrivateData(PrivacyCategory::Location); // GPS in EXIF and XMP, IPTC place names
$tiff->clearPrivateData(PrivacyCategory::All & ~PrivacyCategory::CameraModel); // keep make/model
$webp->clearPrivateData(PrivacyCategory::Identity | PrivacyCategory::Location); // people + place
$gif->clearPrivateData(PrivacyCategory::Provenance); // where, when, and with what software
// Or scrub a single carrier when that is what you hold.
$exif = $jpeg->getEXIF();
$exif->clearPrivateData(PrivacyCategory::MakerNote | PrivacyCategory::Thumbnail);
$jpeg->setEXIF($exif);
$exif = $jpeg->getEXIF();
$bytes = $exif->toBinary();
$exif->getReservedSpaces(); // [[offset, length], …] — the pinned maker notes / private IFDs
$exif->getFreeSpaces(); // the complement: every editable range, in offset order
// Rewrite only the free regions; the reserved bytes are never addressed.
foreach ($exif->getFreeSpaces() as [$offset, $length]) {
$bytes = substr_replace($bytes, $patchFor($offset, $length), $offset, $length);
}
use Belisoful\Image\Meta\XMP;
$xmp = $jpeg->getXMP() ?? XMP::blank();
$xmp->getTitle(); // x-default
$xmp->getTitle('de'); // language alternative
$xmp->setLangAlt(XMP::NS_DC, 'title', ['x-default' => 'Sunset', 'de' => 'Sonnenuntergang']);
$xmp->setKeywords(['sunset', 'norway']);
$xmp->getByPath('xmpMM:History[1]/stEvt:action'); // 'created'
$xmp->addArrayItem(XMP::NS_MM, 'History', ['stEvt:action' => 'edited'], 'Seq');
$xmp->getProperties(); // every property, by prefix:name
$jpeg->setXMP($xmp); // extended XMP when > 64 KB
$png->setXMP($xmp); // PNG iTXt
$webp->setXMP($xmp); // WebP XMP chunk (adds VP8X)
use Belisoful\Image\Meta\PhotoshopFileInfo;
$info = PhotoshopFileInfo::fromJpeg($jpeg); // merged view: XMP, then IPTC, then EXIF
$info['title'] = 'Sunset over Bergen';
$info['copyrightstatus'] = PhotoshopFileInfo::Copyrighted;
$info->applyTo($jpeg); // EXIF + XMP + IRB/IPTC updated together
$jpeg->save('photo-out.jpg');
use Belisoful\Image\GIFImage;
use Belisoful\Image\GIF\GIFFrame;
$gif = GIFImage::fromFile('animation.gif');
$gif->getFrameCount(); // 12
$gif->getLoopCount(); // 0 = forever, null = play once
$gif->getComments(); // ['made with php-image']
$frame = $gif->getFrame(0);
[$frame->getLeft(), $frame->getTop()]; // the sub-rectangle, not the canvas
$frame->getDelayTime(); // hundredths of a second
$frame->getDisposalMethod(); // GIFFrame::DisposalRestoreBackground
$frame->getTransparentIndex(); // palette index, or null
$frame->getPixels(); // one index byte per pixel, de-interlaced
$frame->setDelayTime(8); // retime the animation
$gif->setLoopCount(0); // loop forever
$gif->save('animation-out.gif'); // every other byte identical
$image = $gif->getFrameImage(0); // \GdImage or \Imagick
$frame = new GIFFrame();
$frame->setDelayTime(10);
$frame->setImage($photo); // quantized to a local color table
$gif->addFrame($frame);
$still = GIFImage::fromImage($photo); // a single-frame GIF from any image
use Belisoful\Image\PNGImage;
use Belisoful\Image\ImageChunk;
use Belisoful\Image\PNG\PNGChunkType;
$png = PNGImage::fromFile('image.png');
$png->setICCProfile($iccBytes); // deflated iCCP chunk
$png->setEXIF($exif); // eXIf chunk (bare TIFF)
$png->setXMP($xmp); // iTXt XMP packet
$png->setIPTC($iptc); // stored in the 8BIM text chunk
$png->getICCProfile(); // and each reads straight back
$png->getPhotoshopIRB(); // the resource block the IPTC rides in
// Raw chunk access keeps the spec's normative order and recomputes each CRC.
$png->setChunk(new ImageChunk(PNGChunkType::Gamma, 4, 0, pack('N', 45455)));
$png->removeChunk(PNGChunkType::Text);
$image = $png->getImage(); // decode the raster
$png->setImage($resized); // re-encode, carrying the metadata chunks across
$png->save('image-out.png');
use Belisoful\Image\PNG\APNGFrame;
$apng = PNGImage::fromApngImages([$frame0, $frame1, $frame2], 0.15, 0); // 0.15s each, loop forever
$apng->getIsAnimated(); // true
$apng->getFrameCount(); // 3
$apng->getPlayCount(); // 0 = forever
$frames = $apng->getApngFrames(); // APNGFrame[] with fcTL geometry + delay
$frames[1]->setDelaySeconds(0.5);
$frames[1]->setDisposeOp(APNGFrame::DisposeBackground);
$apng->setApngFrames($frames); // rebuilds acTL/fcTL/fdAT, renumbering sequences
$image = $apng->getApngFrameImage(0); // decode one frame to a \GdImage or \Imagick
$apng->addApngFrame($extra, 0.2); // append a frame from an image
$apng->save('animation.png'); // a still viewer shows the default image
use Belisoful\Image\WebPImage;
$webp = WebPImage::fromFile('image.webp');
$webp->setICCProfile($iccBytes); // adds VP8X + the ICC flag if absent
$webp->setEXIF($exif);
$webp->setXMP($xmp);
$webp->getEXIF()?->getValueByName('Model');
try {
$webp->setIPTC($iptc); // throws: WebP has no IPTC carrier
} catch (\RuntimeException $e) {
// put the equivalent properties in XMP instead
}
$webp->setImage($resized, 80); // re-encode the bitstream, metadata carried across
$webp->save('image-out.webp');
use Belisoful\Image\ImageGraphicsLibraryInterface;
use Belisoful\Image\ImageGraphics;
use Belisoful\Image\ImageGraphicsMode;
$jfif->setImage($imagickThumbnail); // an \Imagick source works anywhere
$gd = $jfif->getImage(ImageGraphicsMode::GD); // ... and either library comes back out
$im = $jfif->getImage(ImageGraphicsMode::Imagick);
ImageGraphics::setDefaultMode(ImageGraphicsMode::Imagick); // prefer ImageMagick for this app
$image = $jpeg->getIPTC()->getRasterizedCaptionImage(); // now an \Imagick by default
ImageGraphics::supports(ImageGraphicsLibraryInterface::CapabilityCmyk); // the default library
ImageGraphics::supports(ImageGraphicsLibraryInterface::CapabilityWebP, ImageGraphicsMode::GD);
ImageGraphics::getLibrary(ImageGraphicsMode::Imagick)->supports(ImageGraphicsLibraryInterface::CapabilityHighBitDepth);
$webp = ImageGraphics::encode($image, ImageGraphicsLibraryInterface::FormatWebP, 80); // false when unsupported
$png = ImageGraphics::encode($image, ImageGraphicsLibraryInterface::FormatPng); // lossless: quality ignored
$cmyk = ImageGraphics::cmykPixels($image); // 4 bytes/pixel
$image = ImageGraphics::fromCmykPixels($cmyk, $w, $h);
$profile = ImageGraphics::getICCProfile($image); // ImageMagick only; null under GD
ImageGraphics::setICCProfile($image, $profile); // over an existing profile: a transform
ImageGraphics::transformICCProfile($image, $sRgb, $adobeRgb); // works in either backend
$library = ImageGraphics::getCapableLibrary(ImageGraphicsLibraryInterface::CapabilityICCEmbed);
$image = $library?->fromRgbPixels($rgb, $w, $h); // built where the profile can ride along
use Belisoful\Image\Compression\GIFLZWCompressor;
use Belisoful\Image\Compression\PackBitsCompressor;
$lzw = new GIFLZWCompressor();
$bytes = $lzw->compress($indexStream, 8); // GIF image data sub-blocks
$data = $lzw->decompress($bytes, 8);
$packed = (new PackBitsCompressor())->compress($scanline);
use Belisoful\Image\RIFFContainer;
$riff = RIFFContainer::fromFile('audio.wav');
$riff->getFormType(); // 'WAVE'
foreach ($riff->getChunks() as $chunk) {
[$chunk->getType(), $chunk->getSize(), $chunk->getOffset()];
}
sh
composer install
composer unittest # tests
vendor/bin/php-cs-fixer fix src # code style (tabs)
vendor/bin/php-cs-fixer fix tests # (the finder excludes tests/, so name it)
vendor/bin/phpstan analyse --memory-limit=1G # static analysis (level 4)
sh
XDEBUG_MODE=coverage vendor/bin/phpunit --testsuite unit --coverage-clover build/logs/clover.xml
php tests/test_tools/coverage-gate.php build/logs/clover.xml
sh
XDEBUG_MODE=coverage php -d memory_limit=10G vendor/bin/phpunit --testsuite unit \
--path-coverage --coverage-php build/logs/coverage.php
php -d memory_limit=8G tests/test_tools/branch-gate.php build/logs/coverage.php
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.