Passed
Push — analysis-lKxmdw ( 194467 )
by Arnaud
04:59 queued 10s
created

Parsedown::blockFigure()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 4
eloc 17
c 2
b 1
f 0
nc 3
nop 1
dl 0
loc 29
rs 9.7
1
<?php
2
/**
3
 * This file is part of the Cecil/Cecil package.
4
 *
5
 * Copyright (c) Arnaud Ligny <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Cecil\Converter;
12
13
use Cecil\Assets\Asset;
14
use Cecil\Builder;
15
16
class Parsedown extends \ParsedownToC
17
{
18
    /** @var Builder */
19
    protected $builder;
20
21
    /** {@inheritdoc} */
22
    protected $regexAttribute = '(?:[#.][-\w:\\\]+[ ]*|[-\w:\\\]+(?:=(?:["\'][^\n]*?["\']|[^\s]+)?)?[ ]*)';
23
24
    /** Regex to verify there is an image in <figure> block */
25
    private $MarkdownImageRegex = "~^!\[.*?\]\(.*?\)~";
26
27
    public function __construct(Builder $builder)
28
    {
29
        $this->builder = $builder;
30
        if ($this->builder->getConfig()->get('body.images.caption.enabled')) {
31
            $this->BlockTypes['!'][] = 'Figure';
32
        }
33
        parent::__construct(['selectors' => $this->builder->getConfig()->get('body.toc')]);
34
    }
35
36
    /**
37
     * {@inheritdoc}
38
     */
39
    protected function inlineImage($excerpt)
40
    {
41
        $image = parent::inlineImage($excerpt);
42
        if (!isset($image)) {
43
            return null;
44
        }
45
        $image['element']['attributes']['src'] = $imageSource = trim($this->removeQuery($image['element']['attributes']['src']));
46
        $asset = new Asset($this->builder, $imageSource);
47
        $width = $asset->getWidth();
48
        if ($width === false) {
49
            return $image;
50
        }
51
        $image['element']['attributes']['src'] = $asset;
52
        /**
53
         * Should be lazy loaded?
54
         */
55
        if ($this->builder->getConfig()->get('body.images.lazy.enabled')) {
56
            $image['element']['attributes']['loading'] = 'lazy';
57
        }
58
        /**
59
         * Should be resized?
60
         */
61
        $imageResized = null;
62
        if (array_key_exists('width', $image['element']['attributes'])
63
            && (int) $image['element']['attributes']['width'] < $width
64
            && $this->builder->getConfig()->get('body.images.resize.enabled')
65
        ) {
66
            $width = (int) $image['element']['attributes']['width'];
67
68
            try {
69
                $imageResized = $asset->resize($width);
70
            } catch (\Exception $e) {
71
                $this->builder->getLogger()->debug($e->getMessage());
72
73
                return $image;
74
            }
75
            $image['element']['attributes']['src'] = $imageResized;
76
        }
77
        // set width
78
        if (!array_key_exists('width', $image['element']['attributes'])) {
79
            $image['element']['attributes']['width'] = $width;
80
        }
81
        // set height
82
        if (!array_key_exists('height', $image['element']['attributes'])) {
83
            $image['element']['attributes']['height'] = $asset->getHeight();
84
        }
85
        /**
86
         * Should be responsive?
87
         */
88
        if ($this->builder->getConfig()->get('body.images.responsive.enabled')) {
89
            $steps = $this->builder->getConfig()->get('body.images.responsive.width.steps');
90
            $wMin = $this->builder->getConfig()->get('body.images.responsive.width.min');
91
            $wMax = $this->builder->getConfig()->get('body.images.responsive.width.max');
92
            $srcset = '';
93
            for ($i = 1; $i <= $steps; $i++) {
94
                $w = ceil($wMin * $i);
95
                if ($w > $width || $w > $wMax) {
96
                    break;
97
                }
98
                $a = new Asset($this->builder, $imageSource);
99
                $img = $a->resize(intval($w));
100
                $srcset .= sprintf('%s %sw', $img, $w);
101
                if ($i < $steps) {
102
                    $srcset .= ', ';
103
                }
104
            }
105
            $imageDefault = $imageResized ?? $asset;
106
            $srcset .= sprintf('%s %sw', $imageDefault, $width);
107
            // ie: srcset="/img-480.jpg 480w, /img-800.jpg 800w"
108
            $image['element']['attributes']['srcset'] = $srcset;
109
            $image['element']['attributes']['sizes'] = $this->builder->getConfig()->get('body.images.responsive.sizes.default');
110
        }
111
112
        return $image;
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    protected function parseAttributeData($attributeString)
119
    {
120
        $attributes = preg_split('/[ ]+/', $attributeString, -1, PREG_SPLIT_NO_EMPTY);
121
        $Data = [];
122
        $HtmlAtt = [];
123
124
        foreach ($attributes as $attribute) {
125
            switch ($attribute[0]) {
126
                case '#': // ID
127
                    $Data['id'] = substr($attribute, 1);
128
                    break;
129
                case '.': // Classes
130
                    $classes[] = substr($attribute, 1);
131
                    break;
132
                default:  // Attributes
133
                    parse_str($attribute, $parsed);
134
                    $HtmlAtt = array_merge($HtmlAtt, $parsed);
135
            }
136
        }
137
138
        if (isset($classes)) {
139
            $Data['class'] = implode(' ', $classes);
140
        }
141
        if (!empty($HtmlAtt)) {
142
            foreach ($HtmlAtt as $a => $v) {
143
                $Data[$a] = trim($v, '"');
144
            }
145
        }
146
147
        return $Data;
148
    }
149
150
    /**
151
     * Add caption to <figure> block.
152
     */
153
    protected function blockFigure($Line)
154
    {
155
        if (1 !== preg_match($this->MarkdownImageRegex, $Line['text'])) {
156
            return;
157
        }
158
159
        $InlineImage = $this->inlineImage($Line);
160
        if (!isset($InlineImage) || empty($InlineImage['element']['attributes']['title'])) {
161
            return;
162
        }
163
164
        $FigureBlock = [
165
            'element' => [
166
                'name'    => 'figure',
167
                'handler' => 'elements',
168
                'text'    => [
169
                    $InlineImage['element'],
170
                ],
171
            ],
172
        ];
173
        $InlineFigcaption = [
174
            'element' => [
175
                'name' => 'figcaption',
176
                'text' => $InlineImage['element']['attributes']['title'],
177
            ],
178
        ];
179
        $FigureBlock['element']['text'][] = $InlineFigcaption['element'];
180
181
        return $FigureBlock;
182
    }
183
184
    /**
185
     * Removes query string from URL.
186
     */
187
    private function removeQuery(string $path): string
188
    {
189
        return strtok($path, '?');
190
    }
191
}
192