123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- <?php
- namespace Drupal\Core\Layout\Icon;
- use Drupal\Component\Utility\Html;
- /**
- * Builds SVG layout icons.
- */
- class SvgIconBuilder implements IconBuilderInterface {
- /**
- * The machine name of the layout.
- *
- * @var string
- */
- protected $id;
- /**
- * The label of the layout.
- *
- * @var string
- */
- protected $label;
- /**
- * The width of the SVG.
- *
- * @var int
- */
- protected $width = 125;
- /**
- * The height of the SVG.
- *
- * @var int
- */
- protected $height = 150;
- /**
- * The padding between regions.
- *
- * @var int
- */
- protected $padding = 4;
- /**
- * The width of region borders.
- *
- * @var int|null
- */
- protected $strokeWidth = 1;
- /**
- * {@inheritdoc}
- */
- public function build(array $icon_map) {
- $regions = $this->calculateSvgValues($icon_map, $this->width, $this->height, $this->strokeWidth, $this->padding);
- return $this->buildRenderArray($regions, $this->width, $this->height, $this->strokeWidth);
- }
- /**
- * Builds a render array representation of an SVG.
- *
- * @param mixed[] $regions
- * An array keyed by region name, with each element containing the 'height',
- * 'width', and 'x' and 'y' offsets of each region.
- * @param int $width
- * The width of the SVG.
- * @param int $height
- * The height of the SVG.
- * @param int|null $stroke_width
- * The width of region borders.
- *
- * @return array
- * A render array representing a SVG icon.
- */
- protected function buildRenderArray(array $regions, $width, $height, $stroke_width) {
- $build = [
- '#type' => 'html_tag',
- '#tag' => 'svg',
- '#attributes' => [
- 'width' => $width,
- 'height' => $height,
- 'class' => [
- 'layout-icon',
- ],
- ],
- ];
- if ($this->id) {
- $build['#attributes']['class'][] = Html::getClass('layout-icon--' . $this->id);
- }
- if ($this->label) {
- $build['title'] = [
- '#type' => 'html_tag',
- '#tag' => 'title',
- '#value' => $this->label,
- ];
- }
- // Append each polygon to the SVG.
- foreach ($regions as $region => $attributes) {
- // Wrapping with a <g> element allows for metadata to exist alongside the
- // rectangle.
- $build['region'][$region] = [
- '#type' => 'html_tag',
- '#tag' => 'g',
- ];
- $build['region'][$region]['title'] = [
- '#type' => 'html_tag',
- '#tag' => 'title',
- '#value' => $region,
- ];
- // Assemble the rectangle SVG element.
- $build['region'][$region]['rect'] = [
- '#type' => 'html_tag',
- '#tag' => 'rect',
- '#attributes' => [
- 'x' => $attributes['x'],
- 'y' => $attributes['y'],
- 'width' => $attributes['width'],
- 'height' => $attributes['height'],
- 'stroke-width' => $stroke_width,
- 'class' => [
- 'layout-icon__region',
- Html::getClass('layout-icon__region--' . $region),
- ],
- ],
- ];
- }
- return $build;
- }
- /**
- * Calculates the dimensions and offsets of all regions.
- *
- * @param string[][] $rows
- * A two-dimensional array representing the visual output of the layout. See
- * the documentation for the $icon_map parameter of
- * \Drupal\Core\Layout\Icon\IconBuilderInterface::build().
- * @param int $width
- * The width of the SVG.
- * @param int $height
- * The height of the SVG.
- * @param int $stroke_width
- * The width of region borders.
- * @param int $padding
- * The padding between regions.
- *
- * @return mixed[][]
- * An array keyed by region name, with each element containing the 'height',
- * 'width', and 'x' and 'y' offsets of each region.
- */
- protected function calculateSvgValues(array $rows, $width, $height, $stroke_width, $padding) {
- $region_rects = [];
- $row_height = $this->getLength(count($rows), $height, $stroke_width, $padding);
- foreach ($rows as $row => $cols) {
- $column_width = $this->getLength(count($cols), $width, $stroke_width, $padding);
- $vertical_offset = $this->getOffset($row, $row_height, $stroke_width, $padding);
- foreach ($cols as $col => $region) {
- $horizontal_offset = $this->getOffset($col, $column_width, $stroke_width, $padding);
- // Check if this region is new, or already exists in the rectangle.
- if (!isset($region_rects[$region])) {
- $region_rects[$region] = [
- 'x' => $horizontal_offset,
- 'y' => $vertical_offset,
- 'width' => $column_width,
- 'height' => $row_height,
- ];
- }
- else {
- // In order to include the area of the previous region and any padding
- // or border, subtract the calculated offset from the original offset.
- $region_rects[$region]['width'] = $column_width + ($horizontal_offset - $region_rects[$region]['x']);
- $region_rects[$region]['height'] = $row_height + ($vertical_offset - $region_rects[$region]['y']);
- }
- }
- }
- return $region_rects;
- }
- /**
- * Gets the offset for this region.
- *
- * @param int $delta
- * The zero-based delta of the region.
- * @param int $length
- * The height or width of the region.
- * @param int $stroke_width
- * The width of the region borders.
- * @param int $padding
- * The padding between regions.
- *
- * @return int
- * The offset for this region.
- */
- protected function getOffset($delta, $length, $stroke_width, $padding) {
- // Half of the stroke width is drawn outside the dimensions.
- $stroke_width /= 2;
- // For every region in front of this add two strokes, as well as one
- // directly in front.
- $num_of_strokes = 2 * $delta + 1;
- return ($num_of_strokes * $stroke_width) + ($delta * ($length + $padding));
- }
- /**
- * Gets the height or width of a region.
- *
- * @param int $number_of_regions
- * The total number of regions.
- * @param int $length
- * The total height or width of the icon.
- * @param int $stroke_width
- * The width of the region borders.
- * @param int $padding
- * The padding between regions.
- *
- * @return float|int
- * The height or width of a region.
- */
- protected function getLength($number_of_regions, $length, $stroke_width, $padding) {
- if ($number_of_regions === 0) {
- return 0;
- }
- // Half of the stroke width is drawn outside the dimensions.
- $total_stroke = $number_of_regions * $stroke_width;
- // Padding does not precede the first region.
- $total_padding = ($number_of_regions - 1) * $padding;
- // Divide the remaining length by the number of regions.
- return ($length - $total_padding - $total_stroke) / $number_of_regions;
- }
- /**
- * {@inheritdoc}
- */
- public function setId($id) {
- $this->id = $id;
- return $this;
- }
- /**
- * {@inheritdoc}
- */
- public function setLabel($label) {
- $this->label = $label;
- return $this;
- }
- /**
- * {@inheritdoc}
- */
- public function setWidth($width) {
- $this->width = $width;
- return $this;
- }
- /**
- * {@inheritdoc}
- */
- public function setHeight($height) {
- $this->height = $height;
- return $this;
- }
- /**
- * {@inheritdoc}
- */
- public function setPadding($padding) {
- $this->padding = $padding;
- return $this;
- }
- /**
- * {@inheritdoc}
- */
- public function setStrokeWidth($stroke_width) {
- $this->strokeWidth = $stroke_width;
- return $this;
- }
- }
|