crossword-8.x-1.x-dev/modules/crossword_image/src/Plugin/crossword/crossword_image/CrosswordThumbnailBase.php
modules/crossword_image/src/Plugin/crossword/crossword_image/CrosswordThumbnailBase.php
<?php
namespace Drupal\crossword_image\Plugin\crossword\crossword_image;
use Drupal\crossword_image\CrosswordImagePluginBase;
use Drupal\file\FileInterface;
/**
* A base class for thumbnail images of crossword files.
*
* Simple extensions of this can set the image type, square size,
* and line width. More advanced extensions can use the helper functions
* provided on this class to add numerals and circles and fill.
*/
abstract class CrosswordThumbnailBase extends CrosswordImagePluginBase {
/**
* The width of a square in pixels.
*
* @var int
*/
protected $squareSize;
/**
* The width of a line in pixels.
*
* @var int
*/
protected $lineSize;
/**
* {@inheritdoc}
*/
public function createImageResource(FileInterface $file) {
$data = $this->crosswordDataService->getData($file);
$grid = $data['puzzle']['grid'];
$image = $this->buildGrid($grid);
$this->addEmbeddedImages($grid, $image);
return $image;
}
/**
* Make a black grid on white background.
*
* The result of this function can be used by other helper functions
* to add things like fill and numerals.
*
* @param array $grid
* The grid part of the crossword data array.
*
* @return resource
* The most basic crossword thumbnail.
*/
public function buildGrid(array $grid) {
$square_size = $this->squareSize;
$line_size = $this->lineSize;
$width = count($grid[0]) * $square_size + $line_size / 2;
$height = count($grid) * $square_size + $line_size / 2;
$image = @imagecreatetruecolor($width, $height);
$white = imagecolorallocate($image, 255, 255, 255);
foreach ($grid as $row_index => $row) {
foreach ($row as $col_index => $square) {
if ($square['fill'] !== NULL) {
$color = $white;
@imagefilledrectangle($image, $col_index * $square_size + $line_size / 2, $row_index * $square_size + $line_size / 2, ($col_index + 1) * $square_size - $line_size / 2, ($row_index + 1) * $square_size - $line_size / 2, $color);
}
}
}
return $image;
}
/**
* Add circles to an existing image.
*
* @param array $grid
* The grid part of the crossword data array.
* @param resource $image
* The crossword image we are adding to.
*
* @return resource
* The image with added circles.
*/
public function addCircles(array $grid, $image) {
$black = imagecolorallocate($image, 0, 0, 0);
foreach ($grid as $row_index => $row) {
foreach ($row as $col_index => $square) {
if (!empty($square['circle'])) {
$start = -180;
$end = 180;
$height = $width = $this->squareSize;
$x = ($col_index + 0.5) * $this->squareSize;
$y = ($row_index + 0.5) * $this->squareSize;
// Draw two arcs to make the line thicker.
@imagearc($image, $x, $y, $width, $height, $start, $end, $black);
@imagearc($image, $x, $y, $width - 1, $height - 1, $start, $end, $black);
}
}
}
return $image;
}
/**
* Add numerals to an existing image.
*
* @param array $grid
* The grid part of the crossword data array.
* @param resource $image
* The crossword image we are adding to.
*
* @return resource
* The image with added numerals.
*/
public function addNumerals(array $grid, $image) {
$square_size = $this->squareSize;
$line_size = $this->lineSize;
$black = imagecolorallocate($image, 0, 0, 0);
$font = $this->font ?? 3;
foreach ($grid as $row_index => $row) {
foreach ($row as $col_index => $square) {
if (isset($square['numeral'])) {
$color = $black;
@imagestring($image, $font, $col_index * $square_size + $line_size / 2 + $line_size, $row_index * $square_size + $line_size / 2, $square['numeral'], $color);
}
}
}
return $image;
}
/**
* Add fill to an existing image.
*
* @param array $grid
* The grid part of the crossword data array.
* @param resource $image
* The crossword image we are adding to.
* @param bool $only_hints
* TRUE if you only want to add the fill for squares marked as hint.
*
* @return resource
* The image with added fill.
*/
public function addFill(array $grid, $image, $only_hints = FALSE) {
$square_size = $this->squareSize;
$black = imagecolorallocate($image, 0, 0, 0);
$font = $this->extensionList->getPath('crossword_image') . '/fonts/RobotoMono-Regular.ttf';
// The RobotoMono regular has approximately this aspect ratio.
$font_aspect = 1.29;
foreach ($grid as $row_index => $row) {
foreach ($row as $col_index => $square) {
if (isset($square['fill']) && (!$only_hints || isset($square['hint']))) {
if (!empty($square['rebus'])) {
$angle = -45;
// Prescribe a total width for the rebus text.
$text_width = $square_size / sqrt(2);
$size = $text_width / strlen($square['fill']) * $font_aspect;
// Bottom left of text. Kind of magic.
$x = ($col_index + 0.15) * $square_size;
$y = ($row_index + 0.41) * $square_size - sqrt($size) / 4;
}
else {
$size = $this->squareSize / 2;
$angle = 0;
// Bottom left of text.
$x = ($col_index + 0.5) * $square_size - $size / 2 / $font_aspect;
$y = ($row_index + 1) * $square_size - $size / 2;
}
@imagettftext($image, $size, $angle, $x, $y, $black, $font, $square['fill']);
}
}
}
return $image;
}
/**
* Add hints to an existing image.
*
* @param array $grid
* The grid part of the crossword data array.
* @param resource $image
* The crossword image we are adding to.
*
* @return resource
* The image with added hints.
*/
public function addHints(array $grid, $image) {
return $this->addFill($grid, $image, TRUE);
}
/**
* Embeds images into an existing image.
*
* @param array $grid
* The grid part of the crossword data array.
* @param resource $image
* The crossword image we are adding to.
*
* @return resource
* The image with added embedded images.
*/
public function addEmbeddedImages(array $grid, $image) {
$square_size = $this->squareSize;
$line_size = $this->lineSize;
foreach ($grid as $row_index => $row) {
foreach ($row as $col_index => $square) {
if (isset($square['image'])) {
$data = base64_decode($square['image']['data']);
$embedded = imagecreatefromstring($data);
if ($embedded === FALSE) {
continue;
}
$width = $square['image']['width'] * $square_size - $line_size;
$height = $square['image']['width'] * $square_size - $line_size;
$embedded = @imagescale($embedded, $width, $height);
@imagecopy(
$image,
$embedded,
$col_index * $square_size + $line_size / 2,
$row_index * $square_size + $line_size / 2,
0,
0,
$width,
$height
);
imagedestroy($embedded);
}
}
}
return $image;
}
}
