3 * @copyright Copyright (C) 2010-2024, the Friendica project
5 * @license GNU AGPL version 3 or any later version
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 namespace Friendica\Content;
24 use Friendica\Content\Image\Collection\MasonryImageRow;
25 use Friendica\Content\Image\Entity\MasonryImage;
26 use Friendica\Content\Post\Collection\PostMedias;
27 use Friendica\Core\Renderer;
31 public static function getBodyAttachHtml(PostMedias $PostMediaImages): string
35 if ($PostMediaImages->haveDimensions()) {
36 if (count($PostMediaImages) > 1) {
37 $media = self::getHorizontalMasonryHtml($PostMediaImages);
38 } elseif (count($PostMediaImages) == 1) {
39 $media = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/single_with_height_allocation.tpl'), [
40 '$image' => $PostMediaImages[0],
41 '$allocated_height' => $PostMediaImages[0]->getAllocatedHeight(),
42 '$allocated_max_width' => ($PostMediaImages[0]->previewWidth ?? $PostMediaImages[0]->width) . 'px',
46 if (count($PostMediaImages) > 1) {
47 $media = self::getImageGridHtml($PostMediaImages);
48 } elseif (count($PostMediaImages) == 1) {
49 $media = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/single.tpl'), [
50 '$image' => $PostMediaImages[0],
59 * @param PostMedias $images
61 * @throws \Friendica\Network\HTTPException\ServiceUnavailableException
63 private static function getImageGridHtml(PostMedias $images): string
65 // Image for first column (fc) and second column (sc)
69 for ($i = 0; $i < count($images); $i++) {
70 ($i % 2 == 0) ? ($images_fc[] = $images[$i]) : ($images_sc[] = $images[$i]);
73 return Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/grid.tpl'), [
82 * Creates a horizontally masoned gallery with a fixed maximum number of pictures per row.
84 * For each row, we calculate how much of the total width each picture will take depending on their aspect ratio
85 * and how much relative height it needs to accomodate all pictures next to each other with their height normalized.
87 * @param array $images
89 * @throws \Friendica\Network\HTTPException\ServiceUnavailableException
91 private static function getHorizontalMasonryHtml(PostMedias $images): string
93 static $column_size = 2;
96 function (PostMedias $PostMediaImages) {
97 if ($singleImageInRow = count($PostMediaImages) == 1) {
98 $PostMediaImages[] = $PostMediaImages[0];
103 foreach ($PostMediaImages as $PostMediaImage) {
104 if ($PostMediaImage->width && $PostMediaImage->height) {
105 $widths[] = $PostMediaImage->width;
106 $heights[] = $PostMediaImage->height;
108 $widths[] = $PostMediaImage->previewWidth;
109 $heights[] = $PostMediaImage->previewHeight;
113 $maxHeight = max($heights);
115 // Corrected width preserving aspect ratio when all images on a row are the same height
116 $correctedWidths = [];
117 foreach ($widths as $i => $width) {
118 $correctedWidths[] = $width * $maxHeight / $heights[$i];
121 $totalWidth = array_sum($correctedWidths);
125 if ($singleImageInRow) {
126 unset($PostMediaImages[1]);
129 foreach ($PostMediaImages as $i => $PostMediaImage) {
130 $row_images2[] = new MasonryImage(
131 $PostMediaImage->uriId,
132 $PostMediaImage->url,
133 $PostMediaImage->preview,
134 $PostMediaImage->description ?? '',
135 100 * $correctedWidths[$i] / $totalWidth,
136 100 * $maxHeight / $correctedWidths[$i]
140 // This magic value will stay constant for each image of any given row and is ultimately
141 // used to determine the height of the row container relative to the available width.
142 $commonHeightRatio = 100 * $correctedWidths[0] / $totalWidth / ($widths[0] / $heights[0]);
144 return new MasonryImageRow($row_images2, count($row_images2), $commonHeightRatio);
146 $images->chunk($column_size)
149 return Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/horizontal_masonry.tpl'), [
151 '$column_size' => $column_size,