如何使用 PHP ImageMagick / Imagick 从 JSON / 数据数组(宽度、高度、x、y、角度)创建大型(高质量 300 dpi)图像
How to create a large (High Quality 300dpi) image from JSON / array of Data (width,height,x,y,angels) using PHP ImageMagic / Imagick
我使用 FabricJs 在 Canvas 上创建了一个带有一些图片和文本的设计 (270x470),然后我通过 fabricJS [=] 以 JSON 格式导出所有 pictures/text 信息60=]() 方法 现在我需要使用 Imagick 在 PHP 中的高质量 (2790x4560) 图像上重新绘制该设计。
JSON 上面设计的数据数组,其中包含所有对象的信息,如大小、位置、角度等。
{
"width": "2790",
"height": "4560",
"json_data": {
"objects": [{
"type": "image",
"originX": "left",
"originY": "top",
"left": "5",
"top": "105",
"width": "260",
"height": "260",
"scaleX": "1",
"scaleY": "1",
"angle": "0",
"opacity": "1",
"src": "http:\example.com/images/098f20be9fb7b66d00cb573acc771e99.JPG",
}, {
"type": "image",
"originX": "left",
"originY": "top",
"left": "5",
"top": "229.5",
"width": "260",
"height": "11",
"scaleX": "1",
"scaleY": "1",
"angle": "0",
"opacity": "1",
"src": "http:\example.com/images/aeced466089d875a7c0dc2467d179e58.png",
}, {
"type": "image",
"originX": "left",
"originY": "top",
"left": "51.07",
"top": "135.58",
"width": "260",
"height": "11",
"scaleX": "1",
"scaleY": "1",
"angle": "47.41",
"opacity": "1",
"src": "http:\example.com/images/910ce024d984b6419d708354bf3641a3.png",
}, {
"type": "image",
"originX": "left",
"originY": "top",
"left": "139.71",
"top": "104.97",
"width": "260",
"height": "11",
"scaleX": "1",
"scaleY": "1",
"angle": "89.65",
"opacity": "1",
"src": "http:\example.com/images/88e096a82e5f8a503a71233addaff64c.png",
}, {
"type": "image",
"originX": "left",
"originY": "top",
"left": "230.78",
"top": "146.93",
"width": "260",
"height": "11",
"scaleX": "1",
"scaleY": "1",
"angle": "134.98",
"src": "http:\example.com/images/d2c0ec738c1fec827381cfeb600bd87d.png",
}, {
"type": "image",
"originX": "left",
"originY": "top",
"left": "265.01",
"top": "240.19",
"width": "260",
"height": "11",
"scaleX": "1",
"scaleY": "1",
"angle": "179.86",
"opacity": "1",
"src": "http:\example.com/images/3f0bc771261860d917e0ad6d09cb2064.png",
}],
"background": "#FF00FF"
}}
这里是我的 代码片段,用于 使用 JSON dataArray[= 在 PHP 中生成高质量图像18=]
error_reporting(E_ALL | E_STRICT);
try {
$id = $_GET['id']; // Design ID
define('DS', DIRECTORY_SEPARATOR);
$jsonDir = dirname(__FILE__) . DS . 'media' . DS . 'designs';
$printData = json_decode(file_get_contents($jsonDir . DS . $id . '.json'));
} catch (Exception $e) {
echo $e->getMessage();
}
try {
$print = new Imagick();
$print->setResolution(300, 300);
$background = (empty($printData->json_data->background)) ? 'transparent' : $printData->json_data->background;
$print->newImage($printData->width, $printData->height, new ImagickPixel($background));
$print->setImageFormat('png32');
$print->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
} catch (Exception $e) {
echo $e->getMessage();
}
// Re-Scaling each Image/Text for Larger Canvas/Image
foreach ($printData->json_data->objects as $i => $object) {
if ($object->type == 'image') {
addImage($object, $print, $printData);
} else {
addText($object, $print, $printData);
}
}
try {
// Saving High Quality Image in (300 dpi)
$fileDir = dirname(__FILE__) . DS . 'media' . DS . 'prints';
if (!file_exists($fileDir) || !is_dir($fileDir)) {
if (!mkdir($fileDir))
die("Could not create directory: {$fileDir}\n");
}
$saved = $print->writeimage($fileDir . DS . $id . '.png');
header('Content-type: image/png');
echo $print;
} catch (Exception $e) {
echo $e->getMessage();
}
addImage();
function addImage($object, $print, $printData) {
try {
$widthScale = ($printData->width / 270);
$heightScale = ($printData->height / 470);
$fileDir = dirname(__FILE__) . DS . 'media' . DS . 'original' . DS;
$src = new Imagick($fileDir . basename($object->src));
$size = $src->getImageGeometry();
$resizeWidth = ($object->width * $object->scaleX) * $widthScale;
$resizeHeight = ($object->height * $object->scaleY) * $heightScale;
$src->resizeImage($resizeWidth, $resizeHeight, Imagick::FILTER_LANCZOS, 1);
$sizeAfterResize = $src->getImageGeometry();
$src->rotateImage(new ImagickPixel('none'), $object->angle);
$sizeAfterRotate = $src->getImageGeometry();
if (!$object->angle) {
$left = $object->left * $widthScale;
$top = $object->top * $heightScale;
} else {
switch ($object->angle) {
case $object->angle > 315:
$left = ($object->left * $widthScale);
$top = ($object->top * $heightScale);
break;
case $object->angle > 270:
$left = ($object->left * $widthScale);
$top = ($object->top * $heightScale);
break;
case $object->angle > 225:
$left = ($object->left * $widthScale);
$top = ($object->top * $heightScale);
break;
case $object->angle > 180:
$left = ($object->left * $widthScale);
$top = ($object->top * $heightScale);
break;
case $object->angle > 135:
$left = ($object->left * $widthScale);
$top = ($object->top * $heightScale);
break;
case $object->angle > 90:
$left = ($object->left * $heightScale) - ($sizeAfterRotate['width'] / 2);
$top = ($object->top * $heightScale) - ($sizeAfterRotate['width'] / 2);
break;
case $object->angle > 45:
$left = ($object->left * $widthScale) - $size['height'] * $widthScale;
$top = ($object->top * $heightScale) - $size['height'] * $heightScale;
break;
default:
$left = $object->left * $widthScale;
$top = $object->top * $heightScale;
break;
}
}
$print->compositeImage($src, Imagick::COMPOSITE_DEFAULT, $left, $top);
} catch (Exception $e) {
echo $e->getMessage();
}
}
我的输出结果 (90%) 与上述解决方案相同,但正如我们所见,一些图像(蓝色数字线)没有放置在应该看起来像第一个设计图像的确切位置
基本上我想做的是,“在循环中调用 addImage 方法进行缩放 - 旋转 - 将每个图像定位到打印图像 (300DPi)
我不确定在 Imagick 中旋转图像后我缺少什么来获得精确的偏移量(新的 x,y coordinates/position/Left-Top)
或者
我在 Scale 之后旋转对象然后组合
或
可能是像 Math.PI :)
这样的数学公式
问题是:
如何根据缩放后的旋转Degree/Angle计算新的offset/Position?
希望发布的片段对大家有用。
这不是一个完整的答案,但你的做法是完全错误的。
Fabric.js 已经可以使用 canvas.toSVG() 函数将 canvas 保存为 SVG 格式。 Imagick 可以打开 SVG 文件并将它们转换为您想要的任何质量的 PNG。
尝试包含图像中包含的位图时会出现问题,例如
"src": "http:\example.com/images/3f0bc771261860d917e0ad6d09cb2064.png",
我强烈建议您自己在服务器上下载这些,而不是让 Imagick 下载它们。这不仅可以让您更好地控制可能发生的任何错误,还可以限制一些安全风险。允许人们从您的服务器中下载任意数据,然后将这些数据用于内存访问存在许多错误的库并不是一个好主意。
执行此操作的一般方法是在 fabric.js 创建 SVG 之前将图像的 src 替换为对本地文件名的引用,或者您可以在转换后更 hackily 地执行此操作- 当您执行此替换时,会生成需要从远程服务器下载的文件列表。
实际的实现细节留作 OP 的练习。
顺便说一句,很有可能有人已经这样做了....您是否彻底搜索过 packagist/github?
我想我得到了你要找的东西,会让你有一个函数,几年前使用它从 JSON 字符串生成高质量图像。不过,您必须进行必要的更改。我的输出扩展名是 tiff。 json 字符串是使用 1% 调整大小的 png 版本制作的,稍后我缩放 s、y 值以采用 100% 全尺寸的 psd。
function generateDesignImage($arr_params,$arr_design){
extract($arr_params);
$images = $arr_design['sprites'];
$dir = $arr_design['options']['source_dir'];
$ext = $arr_design['options']['in_ext'];
$side = $arr_design['options']['img_side'];
$out_ext = $arr_design['options']['out_ext'];
// Canvas
$im = new Imagick();
$im->newImage(6000,6000,'transparent');
$im->setImageColorSpace(Imagick::COLORSPACE_CMYK);
//$im->setImageDepth(8);
/********************* Code for image arrangements *************************/
$i =0;
$min_X = $min_Y = 6000;
$max_X = $max_Y = 0;
$scale = 10;
foreach($images as $sprites=>$val){
$var = "img_$i";
$var = new Imagick();
$var->resizeImage($var->getImageWidth()/$scale,$var->getImageHeight()/$scale,Imagick::FILTER_LANCZOS,1,TRUE);
/************ Find out x,y,width and height *********************/
$c_width = $var->getImageWidth()/2;
$c_height = $var->getImageHeight()/2;
$x1 = ($val['x']*$scale/$val['scale'])-$c_width;
$y1 = ($val['y']*$scale/$val['scale'])-$c_height;
$x2 = ($val['x']*$scale/$val['scale'])+$c_width;
$y2 = ($val['y']*$scale/$val['scale'])+$c_height;
$min_X = ($min_X >= $x1)?$x1:$min_X;
$min_Y = ($min_Y >= $y1)?$y1:$min_Y;
$max_X = ($max_X <= $x2)?$x2:$max_X;
$max_Y = ($max_Y <= $y2)?$y2:$max_Y;
/***************************************************************/
$im->compositeImage($var, $var->getImageCompose(), $x1,$y1,imagick::MONTAGEMODE_FRAME);
$i++;
}
/**************************************************************************/
$im->setImageFormat( $out_ext );
/******************Crop to Exact Fit ********************************/
$im->cropImage ( $max_X-$min_X+100,$max_Y-$min_Y+100 ,$min_X-50 ,$min_Y-50 );
/************************************************************************/
$success1 = $im->writeImage( 'Out_image_'.$design_id.'.'.$out_ext);
$var->BorderImage(new ImagickPixel("white") , 5,5);
return $success1;
}
这里我得到了一个解决方案,也许它会帮助像我这样的人
<?php
// AZinkey
ini_set('memory_limit', '1024M'); // may be need increase memory size
ini_set('display_errors', 1); // enable error display
error_reporting(E_ALL); // show all type errors
$id = $_GET['id'];
$file = $id . ".json"; // json file e.g. 1234.json
$printData = json_decode(file_get_contents($file));
$mask = "mask.png"; // a image (4395x4395) which contains 2669x4395 black fill in center
$maskImg = new Imagick($mask);
$d = $maskImg->getImageGeometry();
$maskWidth = $d['width'];
$maskHeight = $d['height'];
// Then reduce any list of integer
$cd = array_reduce(array($maskWidth, 400), 'gcd');
$r1 = $maskWidth / $cd;
$r2 = 400 / $cd;
$newPrintData['r1'] = $r1;
$newPrintData['r2'] = $r2;
try {
$print = new Imagick();
$print->setResolution(300, 300);
$background = (empty($printData->json_data->background)) ? 'transparent' : $printData->json_data->background;
$print->newImage($maskWidth, $maskHeight, new ImagickPixel($background));
$print->setImageMatte(TRUE);
$print->setImageFormat('png32');
$print->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
} catch (Exception $e) {
echo $e->getMessage();
}
// create two array for store text & images information separately
$imageObjects = $textObjects = [];
foreach ($printData->json_data->objects as $object) {
if ($object->type == 'image') {
$imageObjects[] = $object;
} else if ($object->type == 'text') {
$imageObjects[] = $object;
}
}
foreach ($imageObjects as $object) {
addImageToLarge($object, $print, $printData, $newPrintData);
}
foreach ($imageObjects as $object) {
addTextToLarge($object, $print, $printData, $newPrintData);
}
try {
$print->setImageFormat('png');
$saveFile = $id . "_print.json"; // save large image _print.png
file_put_contents($saveFile, $print);
} catch (Exception $e) {
echo $e->getMessage();
exit();
}
function addImageToLarge($object, $print, $printData, $newPrintData) {
try {
$src = new Imagick($object->src);
$size = $src->getImageGeometry();
$resizeWidth = changeDpi(scale($object->width, $newPrintData['r1'], $newPrintData['r2']) * $object->scaleX);
$resizeHeight = changeDpi(scale($object->height, $newPrintData['r1'], $newPrintData['r2']) * $object->scaleY);
$src->resizeImage($resizeWidth, $resizeHeight, Imagick::FILTER_LANCZOS, 1);
$sizeAfterResize = $src->getImageGeometry();
$src->rotateImage(new ImagickPixel('none'), $object->angle);
$sizeAfterRotate = $src->getImageGeometry();
$left = $object->left < 0 ? -1 * abs(changeDpi(scale($object->left, $newPrintData['r1'], $newPrintData['r2']))) : changeDpi(scale($object->left, $newPrintData['r1'], $newPrintData['r2']));
$top = $object->top < 0 ? -1 * abs(changeDpi(scale($object->top, $newPrintData['r1'], $newPrintData['r2']))) : changeDpi(scale($object->top, $newPrintData['r1'], $newPrintData['r2']));
$print->compositeImage($src, Imagick::COMPOSITE_OVER, $left, $top);
} catch (Exception $e) {
echo $e->getMessage();
exit();
}
}
function addTextToLarge($object, $print, $printData, $newPrintData) {
$fnt['Times New Roman'] = "font/times_6.ttf";
$fnt['Arial'] = "font/arial_8.ttf";
$fnt['Arial Black'] = "font/ariblk_8.ttf";
$fnt['Comic Sans MS'] = "font/comic_5.ttf";
$fnt['Courier New'] = "font/cour_5.ttf";
$fnt['Georgia'] = "font/georgia_5.ttf";
$fnt['Impact'] = "font/impact_7.ttf";
$fnt['Lucida Console'] = "font/lucon_3.ttf";
$fnt['Lucida Sans Unicode'] = "font/l_4.ttf";
$fnt['Palatino Linotype'] = "font/pala_7.ttf";
$fnt['Tahoma'] = "font/tahoma_3.ttf";
$fnt['Trebuchet MS'] = "font/trebuc_3.ttf";
$fnt['Verdana'] = "font/verdana_5.ttf";
try {
$line_height_ratio = $object->lineHeight;
$resizeWidth = changeDpi(scale($object->width, $newPrintData['r1'], $newPrintData['r2']) * $object->scaleX);
$resizeHeight = changeDpi(scale($object->height, $newPrintData['r1'], $newPrintData['r2']) * $object->scaleY);
$print2 = new Imagick();
$print2->setResolution(300, 300);
$print2->newImage($resizeWidth, $resizeHeight, "transparent");
$print2->setImageVirtualPixelMethod(imagick::VIRTUALPIXELMETHOD_BACKGROUND);
$print2->setImageFormat('png32');
$print2->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
// Instantiate Imagick utility objects
$draw = new ImagickDraw();
$color = new ImagickPixel($object->fill);
//$starting_font_size = 100*1.33;
$font_size = (($object->fontSize * $resizeWidth) / $object->width);
$draw->setFontWeight(($object->fontWeight == 'bold') ? 600 : 100 );
$draw->setFontStyle(0);
$draw->setFillColor($color);
// Load Font
//$font_size = $starting_font_size;
$draw->setFont($fnt[$object->fontFamily]);
$draw->setFontSize($font_size);
$draw->setTextAntialias(true);
$draw->setGravity(Imagick::GRAVITY_CENTER);
if ($object->stroke) {
$draw->setStrokeColor($object->stroke);
$draw->setStrokeWidth($object->strokeWidth);
$draw->setStrokeAntialias(true); //try with and without
}
$total_height = 0;
// Run until we find a font size that doesn't exceed $max_height in pixels
while (0 == $total_height || $total_height > $resizeHeight) {
if ($total_height > 0) {
$font_size--; // we're still over height, decrement font size and try again
}
$draw->setFontSize($font_size);
// Calculate number of lines / line height
// Props users Sarke / BMiner:
$words = preg_split('%\s%', $object->text, -1, PREG_SPLIT_NO_EMPTY);
$lines = array();
$i = 0;
$line_height = 0;
while (count($words) > 0) {
$metrics = $print2->queryFontMetrics($draw, implode(' ', array_slice($words, 0, ++$i)));
$line_height = max($metrics['textHeight'], $line_height);
if ($metrics['textWidth'] > $resizeWidth || count($words) < $i) {
$lines[] = implode(' ', array_slice($words, 0, --$i));
$words = array_slice($words, $i);
$i = 0;
}
}
$total_height = count($lines) * $line_height * $line_height_ratio;
if ($total_height > 0) {
}
}
// Writes text to image
$x_pos = 0;
$y_pos = 0;
for ($i = 0; $i < count($lines); $i++) {
$print2->annotateImage($draw, $x_pos, $y_pos + ($i * $line_height * $line_height_ratio), $object->angle, $lines[$i]);
}
if ($object->flipX == 1)
$print2->flopImage(); // x
if ($object->flipY == 1)
$print2->flipImage(); // y
$print2->trimImage(0);
$print2->setImagePage(0, 0, 0, 0);
$print2->resizeImage($resizeWidth, 0, Imagick::FILTER_CATROM, 0.9, false);
$left = $object->left < 0 ? -1 * abs(changeDpi(scale($object->left, $newPrintData['r1'], $newPrintData['r2']))) : changeDpi(scale($object->left, $newPrintData['r1'], $newPrintData['r2']));
$top = $object->top < 0 ? -1 * abs(changeDpi(scale($object->top, $newPrintData['r1'], $newPrintData['r2']))) : changeDpi(scale($object->top, $newPrintData['r1'], $newPrintData['r2']));
$print->compositeImage($print2, Imagick::COMPOSITE_OVER, $left, $top);
//header("Content-Type: image/png");
//echo $print2;exit;
} catch (Exception $e) {
echo $e->getMessage();
exit();
}
}
//The greatest common divisor (GCD)
function gcd($a, $b) {
return $b ? gcd($b, $a % $b) : $a;
}
function changeDpi($px) {
//return ($px/96)*300;
return $px;
}
function scale($px, $r1, $r2) {
return $px * $r1 / $r2;
}
我使用 FabricJs 在 Canvas 上创建了一个带有一些图片和文本的设计 (270x470),然后我通过 fabricJS [=] 以 JSON 格式导出所有 pictures/text 信息60=]() 方法 现在我需要使用 Imagick 在 PHP 中的高质量 (2790x4560) 图像上重新绘制该设计。
JSON 上面设计的数据数组,其中包含所有对象的信息,如大小、位置、角度等。
{
"width": "2790",
"height": "4560",
"json_data": {
"objects": [{
"type": "image",
"originX": "left",
"originY": "top",
"left": "5",
"top": "105",
"width": "260",
"height": "260",
"scaleX": "1",
"scaleY": "1",
"angle": "0",
"opacity": "1",
"src": "http:\example.com/images/098f20be9fb7b66d00cb573acc771e99.JPG",
}, {
"type": "image",
"originX": "left",
"originY": "top",
"left": "5",
"top": "229.5",
"width": "260",
"height": "11",
"scaleX": "1",
"scaleY": "1",
"angle": "0",
"opacity": "1",
"src": "http:\example.com/images/aeced466089d875a7c0dc2467d179e58.png",
}, {
"type": "image",
"originX": "left",
"originY": "top",
"left": "51.07",
"top": "135.58",
"width": "260",
"height": "11",
"scaleX": "1",
"scaleY": "1",
"angle": "47.41",
"opacity": "1",
"src": "http:\example.com/images/910ce024d984b6419d708354bf3641a3.png",
}, {
"type": "image",
"originX": "left",
"originY": "top",
"left": "139.71",
"top": "104.97",
"width": "260",
"height": "11",
"scaleX": "1",
"scaleY": "1",
"angle": "89.65",
"opacity": "1",
"src": "http:\example.com/images/88e096a82e5f8a503a71233addaff64c.png",
}, {
"type": "image",
"originX": "left",
"originY": "top",
"left": "230.78",
"top": "146.93",
"width": "260",
"height": "11",
"scaleX": "1",
"scaleY": "1",
"angle": "134.98",
"src": "http:\example.com/images/d2c0ec738c1fec827381cfeb600bd87d.png",
}, {
"type": "image",
"originX": "left",
"originY": "top",
"left": "265.01",
"top": "240.19",
"width": "260",
"height": "11",
"scaleX": "1",
"scaleY": "1",
"angle": "179.86",
"opacity": "1",
"src": "http:\example.com/images/3f0bc771261860d917e0ad6d09cb2064.png",
}],
"background": "#FF00FF"
}}
这里是我的 代码片段,用于 使用 JSON dataArray[= 在 PHP 中生成高质量图像18=]
error_reporting(E_ALL | E_STRICT);
try {
$id = $_GET['id']; // Design ID
define('DS', DIRECTORY_SEPARATOR);
$jsonDir = dirname(__FILE__) . DS . 'media' . DS . 'designs';
$printData = json_decode(file_get_contents($jsonDir . DS . $id . '.json'));
} catch (Exception $e) {
echo $e->getMessage();
}
try {
$print = new Imagick();
$print->setResolution(300, 300);
$background = (empty($printData->json_data->background)) ? 'transparent' : $printData->json_data->background;
$print->newImage($printData->width, $printData->height, new ImagickPixel($background));
$print->setImageFormat('png32');
$print->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
} catch (Exception $e) {
echo $e->getMessage();
}
// Re-Scaling each Image/Text for Larger Canvas/Image
foreach ($printData->json_data->objects as $i => $object) {
if ($object->type == 'image') {
addImage($object, $print, $printData);
} else {
addText($object, $print, $printData);
}
}
try {
// Saving High Quality Image in (300 dpi)
$fileDir = dirname(__FILE__) . DS . 'media' . DS . 'prints';
if (!file_exists($fileDir) || !is_dir($fileDir)) {
if (!mkdir($fileDir))
die("Could not create directory: {$fileDir}\n");
}
$saved = $print->writeimage($fileDir . DS . $id . '.png');
header('Content-type: image/png');
echo $print;
} catch (Exception $e) {
echo $e->getMessage();
}
addImage();
function addImage($object, $print, $printData) {
try {
$widthScale = ($printData->width / 270);
$heightScale = ($printData->height / 470);
$fileDir = dirname(__FILE__) . DS . 'media' . DS . 'original' . DS;
$src = new Imagick($fileDir . basename($object->src));
$size = $src->getImageGeometry();
$resizeWidth = ($object->width * $object->scaleX) * $widthScale;
$resizeHeight = ($object->height * $object->scaleY) * $heightScale;
$src->resizeImage($resizeWidth, $resizeHeight, Imagick::FILTER_LANCZOS, 1);
$sizeAfterResize = $src->getImageGeometry();
$src->rotateImage(new ImagickPixel('none'), $object->angle);
$sizeAfterRotate = $src->getImageGeometry();
if (!$object->angle) {
$left = $object->left * $widthScale;
$top = $object->top * $heightScale;
} else {
switch ($object->angle) {
case $object->angle > 315:
$left = ($object->left * $widthScale);
$top = ($object->top * $heightScale);
break;
case $object->angle > 270:
$left = ($object->left * $widthScale);
$top = ($object->top * $heightScale);
break;
case $object->angle > 225:
$left = ($object->left * $widthScale);
$top = ($object->top * $heightScale);
break;
case $object->angle > 180:
$left = ($object->left * $widthScale);
$top = ($object->top * $heightScale);
break;
case $object->angle > 135:
$left = ($object->left * $widthScale);
$top = ($object->top * $heightScale);
break;
case $object->angle > 90:
$left = ($object->left * $heightScale) - ($sizeAfterRotate['width'] / 2);
$top = ($object->top * $heightScale) - ($sizeAfterRotate['width'] / 2);
break;
case $object->angle > 45:
$left = ($object->left * $widthScale) - $size['height'] * $widthScale;
$top = ($object->top * $heightScale) - $size['height'] * $heightScale;
break;
default:
$left = $object->left * $widthScale;
$top = $object->top * $heightScale;
break;
}
}
$print->compositeImage($src, Imagick::COMPOSITE_DEFAULT, $left, $top);
} catch (Exception $e) {
echo $e->getMessage();
}
}
我的输出结果 (90%) 与上述解决方案相同,但正如我们所见,一些图像(蓝色数字线)没有放置在应该看起来像第一个设计图像的确切位置
基本上我想做的是,“在循环中调用 addImage 方法进行缩放 - 旋转 - 将每个图像定位到打印图像 (300DPi)
我不确定在 Imagick 中旋转图像后我缺少什么来获得精确的偏移量(新的 x,y coordinates/position/Left-Top) 或者 我在 Scale 之后旋转对象然后组合
或 可能是像 Math.PI :)
这样的数学公式问题是: 如何根据缩放后的旋转Degree/Angle计算新的offset/Position?
希望发布的片段对大家有用。
这不是一个完整的答案,但你的做法是完全错误的。
Fabric.js 已经可以使用 canvas.toSVG() 函数将 canvas 保存为 SVG 格式。 Imagick 可以打开 SVG 文件并将它们转换为您想要的任何质量的 PNG。
尝试包含图像中包含的位图时会出现问题,例如
"src": "http:\example.com/images/3f0bc771261860d917e0ad6d09cb2064.png",
我强烈建议您自己在服务器上下载这些,而不是让 Imagick 下载它们。这不仅可以让您更好地控制可能发生的任何错误,还可以限制一些安全风险。允许人们从您的服务器中下载任意数据,然后将这些数据用于内存访问存在许多错误的库并不是一个好主意。
执行此操作的一般方法是在 fabric.js 创建 SVG 之前将图像的 src 替换为对本地文件名的引用,或者您可以在转换后更 hackily 地执行此操作- 当您执行此替换时,会生成需要从远程服务器下载的文件列表。
实际的实现细节留作 OP 的练习。
顺便说一句,很有可能有人已经这样做了....您是否彻底搜索过 packagist/github?
我想我得到了你要找的东西,会让你有一个函数,几年前使用它从 JSON 字符串生成高质量图像。不过,您必须进行必要的更改。我的输出扩展名是 tiff。 json 字符串是使用 1% 调整大小的 png 版本制作的,稍后我缩放 s、y 值以采用 100% 全尺寸的 psd。
function generateDesignImage($arr_params,$arr_design){
extract($arr_params);
$images = $arr_design['sprites'];
$dir = $arr_design['options']['source_dir'];
$ext = $arr_design['options']['in_ext'];
$side = $arr_design['options']['img_side'];
$out_ext = $arr_design['options']['out_ext'];
// Canvas
$im = new Imagick();
$im->newImage(6000,6000,'transparent');
$im->setImageColorSpace(Imagick::COLORSPACE_CMYK);
//$im->setImageDepth(8);
/********************* Code for image arrangements *************************/
$i =0;
$min_X = $min_Y = 6000;
$max_X = $max_Y = 0;
$scale = 10;
foreach($images as $sprites=>$val){
$var = "img_$i";
$var = new Imagick();
$var->resizeImage($var->getImageWidth()/$scale,$var->getImageHeight()/$scale,Imagick::FILTER_LANCZOS,1,TRUE);
/************ Find out x,y,width and height *********************/
$c_width = $var->getImageWidth()/2;
$c_height = $var->getImageHeight()/2;
$x1 = ($val['x']*$scale/$val['scale'])-$c_width;
$y1 = ($val['y']*$scale/$val['scale'])-$c_height;
$x2 = ($val['x']*$scale/$val['scale'])+$c_width;
$y2 = ($val['y']*$scale/$val['scale'])+$c_height;
$min_X = ($min_X >= $x1)?$x1:$min_X;
$min_Y = ($min_Y >= $y1)?$y1:$min_Y;
$max_X = ($max_X <= $x2)?$x2:$max_X;
$max_Y = ($max_Y <= $y2)?$y2:$max_Y;
/***************************************************************/
$im->compositeImage($var, $var->getImageCompose(), $x1,$y1,imagick::MONTAGEMODE_FRAME);
$i++;
}
/**************************************************************************/
$im->setImageFormat( $out_ext );
/******************Crop to Exact Fit ********************************/
$im->cropImage ( $max_X-$min_X+100,$max_Y-$min_Y+100 ,$min_X-50 ,$min_Y-50 );
/************************************************************************/
$success1 = $im->writeImage( 'Out_image_'.$design_id.'.'.$out_ext);
$var->BorderImage(new ImagickPixel("white") , 5,5);
return $success1;
}
这里我得到了一个解决方案,也许它会帮助像我这样的人
<?php
// AZinkey
ini_set('memory_limit', '1024M'); // may be need increase memory size
ini_set('display_errors', 1); // enable error display
error_reporting(E_ALL); // show all type errors
$id = $_GET['id'];
$file = $id . ".json"; // json file e.g. 1234.json
$printData = json_decode(file_get_contents($file));
$mask = "mask.png"; // a image (4395x4395) which contains 2669x4395 black fill in center
$maskImg = new Imagick($mask);
$d = $maskImg->getImageGeometry();
$maskWidth = $d['width'];
$maskHeight = $d['height'];
// Then reduce any list of integer
$cd = array_reduce(array($maskWidth, 400), 'gcd');
$r1 = $maskWidth / $cd;
$r2 = 400 / $cd;
$newPrintData['r1'] = $r1;
$newPrintData['r2'] = $r2;
try {
$print = new Imagick();
$print->setResolution(300, 300);
$background = (empty($printData->json_data->background)) ? 'transparent' : $printData->json_data->background;
$print->newImage($maskWidth, $maskHeight, new ImagickPixel($background));
$print->setImageMatte(TRUE);
$print->setImageFormat('png32');
$print->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
} catch (Exception $e) {
echo $e->getMessage();
}
// create two array for store text & images information separately
$imageObjects = $textObjects = [];
foreach ($printData->json_data->objects as $object) {
if ($object->type == 'image') {
$imageObjects[] = $object;
} else if ($object->type == 'text') {
$imageObjects[] = $object;
}
}
foreach ($imageObjects as $object) {
addImageToLarge($object, $print, $printData, $newPrintData);
}
foreach ($imageObjects as $object) {
addTextToLarge($object, $print, $printData, $newPrintData);
}
try {
$print->setImageFormat('png');
$saveFile = $id . "_print.json"; // save large image _print.png
file_put_contents($saveFile, $print);
} catch (Exception $e) {
echo $e->getMessage();
exit();
}
function addImageToLarge($object, $print, $printData, $newPrintData) {
try {
$src = new Imagick($object->src);
$size = $src->getImageGeometry();
$resizeWidth = changeDpi(scale($object->width, $newPrintData['r1'], $newPrintData['r2']) * $object->scaleX);
$resizeHeight = changeDpi(scale($object->height, $newPrintData['r1'], $newPrintData['r2']) * $object->scaleY);
$src->resizeImage($resizeWidth, $resizeHeight, Imagick::FILTER_LANCZOS, 1);
$sizeAfterResize = $src->getImageGeometry();
$src->rotateImage(new ImagickPixel('none'), $object->angle);
$sizeAfterRotate = $src->getImageGeometry();
$left = $object->left < 0 ? -1 * abs(changeDpi(scale($object->left, $newPrintData['r1'], $newPrintData['r2']))) : changeDpi(scale($object->left, $newPrintData['r1'], $newPrintData['r2']));
$top = $object->top < 0 ? -1 * abs(changeDpi(scale($object->top, $newPrintData['r1'], $newPrintData['r2']))) : changeDpi(scale($object->top, $newPrintData['r1'], $newPrintData['r2']));
$print->compositeImage($src, Imagick::COMPOSITE_OVER, $left, $top);
} catch (Exception $e) {
echo $e->getMessage();
exit();
}
}
function addTextToLarge($object, $print, $printData, $newPrintData) {
$fnt['Times New Roman'] = "font/times_6.ttf";
$fnt['Arial'] = "font/arial_8.ttf";
$fnt['Arial Black'] = "font/ariblk_8.ttf";
$fnt['Comic Sans MS'] = "font/comic_5.ttf";
$fnt['Courier New'] = "font/cour_5.ttf";
$fnt['Georgia'] = "font/georgia_5.ttf";
$fnt['Impact'] = "font/impact_7.ttf";
$fnt['Lucida Console'] = "font/lucon_3.ttf";
$fnt['Lucida Sans Unicode'] = "font/l_4.ttf";
$fnt['Palatino Linotype'] = "font/pala_7.ttf";
$fnt['Tahoma'] = "font/tahoma_3.ttf";
$fnt['Trebuchet MS'] = "font/trebuc_3.ttf";
$fnt['Verdana'] = "font/verdana_5.ttf";
try {
$line_height_ratio = $object->lineHeight;
$resizeWidth = changeDpi(scale($object->width, $newPrintData['r1'], $newPrintData['r2']) * $object->scaleX);
$resizeHeight = changeDpi(scale($object->height, $newPrintData['r1'], $newPrintData['r2']) * $object->scaleY);
$print2 = new Imagick();
$print2->setResolution(300, 300);
$print2->newImage($resizeWidth, $resizeHeight, "transparent");
$print2->setImageVirtualPixelMethod(imagick::VIRTUALPIXELMETHOD_BACKGROUND);
$print2->setImageFormat('png32');
$print2->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
// Instantiate Imagick utility objects
$draw = new ImagickDraw();
$color = new ImagickPixel($object->fill);
//$starting_font_size = 100*1.33;
$font_size = (($object->fontSize * $resizeWidth) / $object->width);
$draw->setFontWeight(($object->fontWeight == 'bold') ? 600 : 100 );
$draw->setFontStyle(0);
$draw->setFillColor($color);
// Load Font
//$font_size = $starting_font_size;
$draw->setFont($fnt[$object->fontFamily]);
$draw->setFontSize($font_size);
$draw->setTextAntialias(true);
$draw->setGravity(Imagick::GRAVITY_CENTER);
if ($object->stroke) {
$draw->setStrokeColor($object->stroke);
$draw->setStrokeWidth($object->strokeWidth);
$draw->setStrokeAntialias(true); //try with and without
}
$total_height = 0;
// Run until we find a font size that doesn't exceed $max_height in pixels
while (0 == $total_height || $total_height > $resizeHeight) {
if ($total_height > 0) {
$font_size--; // we're still over height, decrement font size and try again
}
$draw->setFontSize($font_size);
// Calculate number of lines / line height
// Props users Sarke / BMiner:
$words = preg_split('%\s%', $object->text, -1, PREG_SPLIT_NO_EMPTY);
$lines = array();
$i = 0;
$line_height = 0;
while (count($words) > 0) {
$metrics = $print2->queryFontMetrics($draw, implode(' ', array_slice($words, 0, ++$i)));
$line_height = max($metrics['textHeight'], $line_height);
if ($metrics['textWidth'] > $resizeWidth || count($words) < $i) {
$lines[] = implode(' ', array_slice($words, 0, --$i));
$words = array_slice($words, $i);
$i = 0;
}
}
$total_height = count($lines) * $line_height * $line_height_ratio;
if ($total_height > 0) {
}
}
// Writes text to image
$x_pos = 0;
$y_pos = 0;
for ($i = 0; $i < count($lines); $i++) {
$print2->annotateImage($draw, $x_pos, $y_pos + ($i * $line_height * $line_height_ratio), $object->angle, $lines[$i]);
}
if ($object->flipX == 1)
$print2->flopImage(); // x
if ($object->flipY == 1)
$print2->flipImage(); // y
$print2->trimImage(0);
$print2->setImagePage(0, 0, 0, 0);
$print2->resizeImage($resizeWidth, 0, Imagick::FILTER_CATROM, 0.9, false);
$left = $object->left < 0 ? -1 * abs(changeDpi(scale($object->left, $newPrintData['r1'], $newPrintData['r2']))) : changeDpi(scale($object->left, $newPrintData['r1'], $newPrintData['r2']));
$top = $object->top < 0 ? -1 * abs(changeDpi(scale($object->top, $newPrintData['r1'], $newPrintData['r2']))) : changeDpi(scale($object->top, $newPrintData['r1'], $newPrintData['r2']));
$print->compositeImage($print2, Imagick::COMPOSITE_OVER, $left, $top);
//header("Content-Type: image/png");
//echo $print2;exit;
} catch (Exception $e) {
echo $e->getMessage();
exit();
}
}
//The greatest common divisor (GCD)
function gcd($a, $b) {
return $b ? gcd($b, $a % $b) : $a;
}
function changeDpi($px) {
//return ($px/96)*300;
return $px;
}
function scale($px, $r1, $r2) {
return $px * $r1 / $r2;
}