Я создал дизайн (270×470) с некоторыми изображениями и текстом на Canvas с помощью FabricJs, затем я экспортирую все изображения / текстовую информацию в формате JSON с помощью метода canvas.toJSON () fabricJS. Теперь мне нужно перерисовать этот дизайн на высоком качестве ( 2790×4560) в PHP с помощью Imagick.
JSON dataArray для дизайна выше, который содержит всю информацию об объекте, такую как размер, положение, угол и т. Д.
{ "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" }}
И здесь мой фрагмент кода для генерации изображения высокого качества в PHP с использованием JSON dataArray
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 для масштабирования – повернуть – позиционируйте каждое изображение на Print Image (300DPi)
я не уверен, что мне не хватает, чтобы получить точное смещение (новые координаты x, y / позиция / левый верх) после поворота для изображения в Imagick или i am Вращающийся объект после Scale затем скомпонован
или может быть математической формулой, как Math.PI 🙂
Вопрос : Как я могу вычислить новое смещение / положение в соответствии со степенью поворота / угла после шкалы?
Я надеюсь, что опубликованный фрагмент полезен для всех.
Это не полный ответ, но вы идете об этом совершенно неправильно.
У Fabric.js уже есть способ сохранить холст в формате SVG с помощью функции canvas.toSVG () . Imagick может открывать SVG-файлы и преобразовывать их в PNG любого качества.
Будет проблема при попытке включить растровые изображения, которые включены в изображение, например
"src": "http:\\example.com/images/3f0bc771261860d917e0ad6d09cb2064.png",
Я настоятельно рекомендую загрузить их самостоятельно на сервере, вместо того, чтобы позволить Imagick загружать их. Это не только позволит вам лучше контролировать любые ошибки, которые могут возникнуть, но также ограничивает некоторые риски для безопасности. Разрешить пользователям загружать произвольные данные с вашего сервера, а затем использовать эти данные, используемые библиотекой, у которой было много ошибок с доступом к памяти, не очень хорошая идея.
Общий способ сделать это – заменить src изображения ссылкой на локальное имя файла, прежде чем fabric.js создаст SVG, или вы сможете сделать это еще более взломанно после его преобразования – и когда вы сделаете эту замену сгенерировать список файлов, которые необходимо загрузить с удаленного сервера.
Фактические детали реализации остаются в качестве упражнения для 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 eg 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: http://stackoverflow.com/questions/5746537/how-can-i-wrap-text-using-imagick-in-php-so-that-it-is-drawn-as-multiline-text $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; }