Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

PageViewController::getScore()   B
last analyzed

Complexity

Conditions 9
Paths 40

Size

Total Lines 49
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 31
c 0
b 0
f 0
nc 40
nop 2
dl 0
loc 49
rs 8.0555
1
<?php
2
/**
3
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
4
 *
5
 * This file is part of the Kitodo and TYPO3 projects.
6
 *
7
 * @license GNU General Public License version 3 or later.
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11
12
namespace Kitodo\Dlf\Controller;
13
14
use Kitodo\Dlf\Common\AbstractDocument;
15
use Kitodo\Dlf\Common\DocumentAnnotation;
16
use Kitodo\Dlf\Common\Helper;
17
use Kitodo\Dlf\Common\IiifManifest;
18
use Kitodo\Dlf\Common\MetsDocument;
19
use Kitodo\Dlf\Domain\Model\Document;
20
use Kitodo\Dlf\Domain\Model\FormAddDocument;
21
use Psr\Http\Message\ResponseInterface;
22
use TYPO3\CMS\Core\Utility\GeneralUtility;
23
use TYPO3\CMS\Core\Utility\MathUtility;
24
use Ubl\Iiif\Presentation\Common\Model\Resources\ManifestInterface;
25
use Ubl\Iiif\Presentation\Common\Vocabulary\Motivation;
26
27
/**
28
 * Controller class for the plugin 'Page View'.
29
 *
30
 * @package TYPO3
31
 * @subpackage dlf
32
 *
33
 * @access public
34
 */
35
class PageViewController extends AbstractController
36
{
37
    /**
38
     * @access protected
39
     * @var array Holds the controls to add to the map
40
     */
41
    protected array $controls = [];
42
43
    /**
44
     * @access protected
45
     * @var array Holds the current images' URLs and MIME types
46
     */
47
    protected array $images = [];
48
49
    /**
50
     * Holds the current scores' URL, MIME types and the
51
     * id of the current page
52
     *
53
     * @var array
54
     * @access protected
55
     */
56
    protected $scores = [];
57
58
    /**
59
     * @var array
60
     * @access protected
61
     */
62
    protected $measures = [];
63
64
    /**
65
     * Holds the current fulltexts' URLs
66
     *
67
     * @var array
68
     * @access protected
69
     * @var array Holds the current full texts' URLs
70
     */
71
    protected array $fulltexts = [];
72
73
    /**
74
     * Holds the current AnnotationLists / AnnotationPages
75
     *
76
     * @access protected
77
     * @var array Holds the current AnnotationLists / AnnotationPages
78
     */
79
    protected array $annotationContainers = [];
80
81
82
    /**
83
     * Holds the verovio relevant annotations
84
     *
85
     * @var array
86
     */
87
    protected $verovioAnnotations = [];
88
89
    /**
90
     * The main method of the plugin
91
     *
92
     * @access public
93
     *
94
     * @return ResponseInterface the response
95
     */
96
    public function mainAction(): ResponseInterface
97
    {
98
        // Load current document.
99
        $this->loadDocument();
100
        if ($this->isDocMissingOrEmpty()) {
101
            // Quit without doing anything if required variables are not set.
102
            return $this->htmlResponse();
103
        } else {
104
            if (isset($this->settings['multiViewType']) && $this->document->getCurrentDocument()->tableOfContents[0]['type'] === $this->settings['multiViewType'] && empty($this->requestData['multiview'])) {
105
                $params = array_merge(
106
                    ['tx_dlf' => $this->requestData],
107
                    ['tx_dlf[multiview]' => 1]
108
                );
109
                $uriBuilder = $this->uriBuilder;
110
                $uri = $uriBuilder
111
                    ->setArguments($params)
112
                    ->setArgumentPrefix('tx_dlf')
113
                    ->uriFor('main');
114
                $this->redirectToUri($uri);
115
            }
116
            $this->setPage();
117
            $this->requestData['double'] = MathUtility::forceIntegerInRange($this->requestData['double'], 0, 1, 0);
118
119
            $documentAnnotation = DocumentAnnotation::getInstance($this->document);
120
            $this->verovioAnnotations = $documentAnnotation->getVerovioRelevantAnnotations();
121
        }
122
123
        $this->setPage();
124
125
        $page = $this->requestData['page'] ?? 0;
126
127
        // Get image data.
128
        $this->images[0] = $this->getImage($page);
129
        $this->fulltexts[0] = $this->getFulltext($page);
130
        $this->annotationContainers[0] = $this->getAnnotationContainers($page);
131
        if ($this->requestData['double'] && $page < $this->document->getCurrentDocument()->numPages) {
132
            $this->images[1] = $this->getImage($page + 1);
133
            $this->fulltexts[1] = $this->getFulltext($page + 1);
134
            $this->annotationContainers[1] = $this->getAnnotationContainers($page + 1);
135
        }
136
137
        $this->scores = $this->getScore($page);
138
        $this->measures = $this->getMeasures($page);
139
140
        // Get the controls for the map.
141
        $this->controls = explode(',', $this->settings['features']);
142
143
        $this->view->assign('forceAbsoluteUrl', $this->extConf['general']['forceAbsoluteUrl']);
144
145
        $this->addViewerJS();
146
147
        $this->view->assign('docCount', is_array($this->documentArray) ? count($this->documentArray) : 0);
148
        $this->view->assign('docArray', $this->documentArray);
149
        $this->view->assign('docPage', $this->requestData['docPage'] ?? null);
150
        $this->view->assign('docType', $this->document->getCurrentDocument()->tableOfContents[0]['type']);
151
152
        $this->view->assign('multiview', $this->requestData['multiview'] ?? null);
153
        if ($this->requestData['multiview'] ?? false) {
154
            $this->multipageNavigation();
155
        }
156
157
        $this->view->assign('images', $this->images);
158
        $this->view->assign('docId', $this->requestData['id']);
159
        $this->view->assign('page', $page);
160
161
        return $this->htmlResponse();
162
    }
163
164
    /**
165
     * Add multi page navigation
166
     * @return void
167
     */
168
    protected function multipageNavigation(): void
169
    {
170
        $navigationArray = [];
171
        $navigationMeasureArray = [];
172
        $navigateAllPageNext = [];
173
        $navigateAllPagePrev = [];
174
        $navigateAllMeasureNext = [];
175
        $navigateAllMeasurePrev = [];
176
        $docNumPages = [];
177
        $i = 0;
178
        foreach ($this->documentArray as $document) {
179
            // convert either page or measure if requestData exists
180
            if ($this->requestData['docPage'][$i] && empty($this->requestData['docMeasure'][$i])) {
181
                // convert document page information to measure count information
182
                $this->requestData['docMeasure'][$i] = $this->convertMeasureOrPage($document, null, $this->requestData['docPage'][$i]);
183
184
            } elseif ((empty($this->requestData['docPage'][$i]) || $this->requestData['docPage'][$i] === 1) && $this->requestData['docMeasure'][$i]) {
185
                $this->requestData['docPage'][$i] = $this->convertMeasureOrPage($document, $this->requestData['docMeasure'][$i]);
186
            }
187
188
            $navigationArray[$i]['next'] = [
189
                'tx_dlf[docPage][' . $i . ']' =>
190
                    MathUtility::forceIntegerInRange((int) $this->requestData['docPage'][$i] + 1, 1, $document->numPages, 1)
191
            ];
192
            $navigationArray[$i]['prev'] = [
193
                'tx_dlf[docPage][' . $i . ']' =>
194
                    MathUtility::forceIntegerInRange((int) $this->requestData['docPage'][$i] - 1, 1, $document->numPages, 1)
195
            ];
196
197
            $navigateAllPageNext = array_merge(
198
                $navigateAllPageNext,
199
                [
200
                    'tx_dlf[docPage][' . $i . ']' =>
201
                        MathUtility::forceIntegerInRange((int) $this->requestData['docPage'][$i] + 1, 1, $document->numPages, 1)
202
                ]
203
            );
204
205
            $navigateAllPagePrev = array_merge(
206
                $navigateAllPagePrev,
207
                [
208
                    'tx_dlf[docPage][' . $i . ']' =>
209
                        MathUtility::forceIntegerInRange((int) $this->requestData['docPage'][$i] - 1, 1, $document->numPages, 1)
210
                ]
211
            );
212
213
            $navigateAllMeasureNext = array_merge(
214
                $navigateAllMeasureNext,
215
                [
216
                    'tx_dlf[docMeasure][' . $i . ']' =>
217
                        MathUtility::forceIntegerInRange((int) $this->requestData['docMeasure'][$i] + 1, 1, $document->numMeasures, 1)
218
                ]
219
            );
220
221
            $navigateAllMeasurePrev = array_merge(
222
                $navigateAllMeasurePrev,
223
                [
224
                    'tx_dlf[docMeasure][' . $i . ']' =>
225
                        MathUtility::forceIntegerInRange((int) $this->requestData['docMeasure'][$i] - 1, 1, $document->numMeasures, 1)
226
                ]
227
            );
228
229
            if ($document->numMeasures > 0) {
230
                $navigationMeasureArray[$i]['next'] = [
231
                    'tx_dlf[docMeasure][' . $i . ']' =>
232
                        MathUtility::forceIntegerInRange((int) $this->requestData['docMeasure'][$i] + 1, 1, $document->numMeasures, 1)
233
                ];
234
235
                $navigationMeasureArray[$i]['prev'] = [
236
                    'tx_dlf[docMeasure][' . $i . ']' =>
237
                        MathUtility::forceIntegerInRange((int) $this->requestData['docMeasure'][$i] - 1, 1, $document->numMeasures, 1)
238
                ];
239
            }
240
241
            $docNumPages[$i] = $document->numPages;
242
            $i++;
243
        }
244
245
        // page navigation
246
        $this->view->assign('navigationArray', $navigationArray);
247
        $this->view->assign('navigateAllPageNext', $navigateAllPageNext);
248
        $this->view->assign('navigateAllPagePrev', $navigateAllPagePrev);
249
        // measure navigation
250
        $this->view->assign('navigateAllMeasurePrev', $navigateAllMeasurePrev);
251
        $this->view->assign('navigateAllMeasureNext', $navigateAllMeasureNext);
252
        $this->view->assign('navigationMeasureArray', $navigationMeasureArray);
253
254
        $this->view->assign('docNumPage', $docNumPages);
255
    }
256
257
    /**
258
     * Converts either measure into page or page into measure
259
     * @param $document
260
     * @param $measure
261
     * @param $page
262
     * @return false|int|mixed|string|null
263
     */
264
    public function convertMeasureOrPage($document, $measure = null, $page = null)
265
    {
266
        $return = null;
267
        $measure2Page = array_column($document->musicalStructure, 'page');
268
        if ($measure) {
269
            $return = $measure2Page[$measure];
270
        } elseif ($page) {
271
            $return = array_search($page, $measure2Page);
272
        }
273
274
        return $return;
275
    }
276
277
    /**
278
     * Action to add multiple mets sources (multi page view)
279
     * @return ResponseInterface the response
280
     */
281
    public function addDocumentAction(FormAddDocument $formAddDocument): ResponseInterface
282
    {
283
        if (GeneralUtility::isValidUrl($formAddDocument->getLocation())) {
284
            $nextMultipleSourceKey = 0;
285
            if (isset($this->requestData['multipleSource']) && is_array($this->requestData['multipleSource'])) {
286
                $nextMultipleSourceKey = max(array_keys($this->requestData['multipleSource'])) + 1;
287
            }
288
            $params = array_merge(
289
                ['tx_dlf' => $this->requestData],
290
                ['tx_dlf[multipleSource][' . $nextMultipleSourceKey . ']' => $formAddDocument->getLocation()],
291
                ['tx_dlf[multiview]' => 1]
292
            );
293
            $uriBuilder = $this->uriBuilder;
294
            $uri = $uriBuilder
295
                ->setArguments($params)
296
                ->setArgumentPrefix('tx_dlf')
297
                ->uriFor('main');
298
299
            return $this->redirectToUri($uri);
300
        }
301
302
        return $this->htmlResponse();
303
    }
304
305
    /**
306
     * Get all measures from musical struct
307
     * @param int $page
308
     * @param ?MetsDocument $specificDoc
309
     * @param int|null $docNumber
310
     * @return array
311
     */
312
    protected function getMeasures(int $page, MetsDocument $specificDoc = null, $docNumber = null): array
313
    {
314
        if ($specificDoc) {
315
            $doc = $specificDoc;
316
        } else {
317
            $doc = $this->document->getCurrentDocument();
0 ignored issues
show
Bug introduced by
The method getCurrentDocument() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

317
            /** @scrutinizer ignore-call */ 
318
            $doc = $this->document->getCurrentDocument();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
318
        }
319
        $currentPhysId = $doc->physicalStructure[$page];
320
        $measureCoordsFromCurrentSite = [];
321
        $measureCounterToMeasureId = [];
322
        $measureLinks = [];
323
        $defaultFileId = $doc->physicalStructureInfo[$currentPhysId]['files']['DEFAULT'];
324
        if ($doc instanceof MetsDocument) {
325
            if (isset($defaultFileId)) {
326
                $musicalStruct = $doc->musicalStructureInfo;
327
328
                $i = 0;
329
                foreach ($musicalStruct as $measureData) {
330
                    if ($defaultFileId == $measureData['files']['DEFAULT']['fileid']) {
331
                        $measureCoordsFromCurrentSite[$measureData['files']['SCORE']['begin']] = $measureData['files']['DEFAULT']['coords'];
332
                        $measureCounterToMeasureId[$i] = $measureData['files']['SCORE']['begin'];
333
334
                        if ($specificDoc) {
335
                            // build link for each measure
336
                            $params = [
337
                                'tx_dlf' => $this->requestData,
338
                                'tx_dlf[docMeasure][' . $docNumber . ']' => $i
339
                            ];
340
                        } else {
341
                            // build link for each measure
342
                            $params = [
343
                                'tx_dlf' => $this->requestData,
344
                                'tx_dlf[measure]' => $i
345
                            ];
346
                        }
347
                        $uriBuilder = $this->uriBuilder;
348
                        $uri = $uriBuilder
349
                            ->setArguments($params)
350
                            ->setArgumentPrefix('tx_dlf')
351
                            ->uriFor('main');
352
                        $measureLinks[$measureData['files']['SCORE']['begin']] = $uri;
353
354
                    }
355
                    $i++;
356
                }
357
            }
358
        }
359
        return [
360
            'measureCoordsCurrentSite' => $measureCoordsFromCurrentSite,
361
            'measureCounterToMeasureId' => $measureCounterToMeasureId,
362
            'measureLinks' => $measureLinks
363
        ];
364
    }
365
366
    /**
367
     * Get score URL and MIME type
368
     *
369
     * @access protected
370
     *
371
     * @param int $page: Page number
372
     * @param ?MetsDocument $specificDoc
373
     *
374
     * @return array URL and MIME type of fulltext file
375
     */
376
    protected function getScore(int $page, MetsDocument $specificDoc = null)
377
    {
378
        $score = [];
379
        $loc = '';
380
        if ($specificDoc) {
381
            $doc = $specificDoc;
382
        } else {
383
            $doc = $this->document->getCurrentDocument();
384
        }
385
        if ($doc instanceof MetsDocument) {
386
            $fileGrpsScores = GeneralUtility::trimExplode(',', $this->extConf['files']['fileGrpScore']);
387
388
            $pageId = $doc->physicalStructure[$page];
389
            $files = $doc->physicalStructureInfo[$pageId]['files'] ?? [];
390
391
            foreach ($fileGrpsScores as $fileGrpScore) {
392
                if (isset($files[$fileGrpScore])) {
393
                    $loc = $files[$fileGrpScore];
394
                    break;
395
                }
396
            }
397
398
            if (!empty($loc)) {
399
                $score['mimetype'] = $doc->getFileMimeType($loc);
400
                $score['pagebeginning'] = $doc->getPageBeginning($pageId, $loc);
401
                $score['url'] = $doc->getFileLocation($loc);
402
                if ($this->settings['useInternalProxy']) {
403
                    // Configure @action URL for form.
404
                    $uri = $this->uriBuilder->reset()
405
                        ->setTargetPageUid($this->pageUid)
406
                        ->setCreateAbsoluteUri(!empty($this->settings['forceAbsoluteUrl']) ? true : false)
407
                        ->setArguments(
408
                            [
409
                                'eID' => 'tx_dlf_pageview_proxy',
410
                                'url' => $score['url'],
411
                                'uHash' => GeneralUtility::hmac($score['url'], 'PageViewProxy')
412
                            ]
413
                        )
414
                        ->build();
415
416
                    $score['url'] = $uri;
417
                }
418
            }
419
        }
420
421
        if (empty($score)) {
422
            $this->logger->notice('No score file found for page "' . $page . '" in fileGrps "' . ($this->extConf['files']['fileGrpScore'] ?? '') . '"');
423
        }
424
        return $score;
425
    }
426
427
    /**
428
     * Get fulltext URL and MIME type
429
     *
430
     * @access protected
431
     *
432
     * @param int $page Page number
433
     *
434
     * @return array URL and MIME type of fulltext file
435
     */
436
    protected function getFulltext(int $page): array
437
    {
438
        $fulltext = [];
439
        // Get fulltext link.
440
        $fileGrpsFulltext = GeneralUtility::trimExplode(',', $this->extConf['files']['fileGrpFulltext']);
441
        while ($fileGrpFulltext = array_shift($fileGrpsFulltext)) {
442
            $physicalStructureInfo = $this->document->getCurrentDocument()->physicalStructureInfo[$this->document->getCurrentDocument()->physicalStructure[$page]];
443
            $files = $physicalStructureInfo['files'];
444
            if (!empty($files[$fileGrpFulltext])) {
445
                $file = $this->document->getCurrentDocument()->getFileInfo($files[$fileGrpFulltext]);
446
                $fulltext['url'] = $file['location'];
447
                if ($this->settings['useInternalProxy']) {
448
                    $this->configureProxyUrl($fulltext['url']);
449
                }
450
                $fulltext['mimetype'] = $file['mimeType'];
451
                break;
452
            } else {
453
                $this->logger->notice('No full-text file found for page "' . $page . '" in fileGrp "' . $fileGrpFulltext . '"');
454
            }
455
        }
456
        if (empty($fulltext)) {
457
            $this->logger->notice('No full-text file found for page "' . $page . '" in fileGrps "' . $this->extConf['files']['fileGrpFulltext'] . '"');
458
        }
459
        return $fulltext;
460
    }
461
462
    /**
463
     * Adds Viewer javascript
464
     *
465
     * @access protected
466
     *
467
     * @return void
468
     */
469
    protected function addViewerJS(): void
470
    {
471
        if (is_array($this->documentArray) && count($this->documentArray) > 1) {
472
            $jsViewer = 'tx_dlf_viewer = [];';
473
            $i = 0;
474
            foreach ($this->documentArray as $document) {
475
                if ($document !== null) {
476
                    $docPage = $this->requestData['docPage'][$i];
477
                    $docImage = [];
478
                    $docFulltext = [];
479
                    $docAnnotationContainers = [];
480
481
                    if ($this->document->getCurrentDocument() instanceof MetsDocument) {
482
                        // check if page or measure is set
483
                        if ($this->requestData['docMeasure'][$i]) {
484
                            // convert document page information to measure count information
485
                            $measure2Page = array_column($document->musicalStructure, 'page');
486
                            $docPage = $measure2Page[$this->requestData['docMeasure'][$i]];
487
                        }
488
                    }
489
                    if ($docPage == null) {
490
                        $docPage = 1;
491
                    }
492
                    $docImage[0] = $this->getImage($docPage, $document);
493
                    $currentMeasureId = '';
494
495
                    $docScore = $this->getScore($docPage, $document);
496
                    $docMeasures = $this->getMeasures($docPage, $document);
497
498
                    if ($this->requestData['docMeasure'][$i]) {
499
                        $currentMeasureId = $docMeasures['measureCounterToMeasureId'][$this->requestData['docMeasure'][$i]];
500
                    }
501
502
                    $viewer = [
503
                        'controls' => $this->controls,
504
                        'div' => "tx-dfgviewer-map-' . $i . '",
505
                        'progressElementId' => $this->settings['progressElementId'],
506
                        'counter' => $i,
507
                        'images' => $docImage,
508
                        'fulltexts' => $docFulltext,
509
                        'score' => $docScore,
510
                        'annotationContainers' => $docAnnotationContainers,
511
                        'measureCoords' => $docMeasures['measureCoordsCurrentSite'],
512
                        'useInternalProxy' => $this->settings['useInternalProxy'],
513
                        'currentMeasureId' => $currentMeasureId,
514
                        'measureIdLinks' => $docMeasures['measureLinks']
515
                    ];
516
517
                    $jsViewer .= 'tx_dlf_viewer[' . $i . '] = new dlfViewer(' . json_encode($viewer) . ');
518
                            ';
519
                    $i++;
520
                }
521
            }
522
523
            // Viewer configuration.
524
            $viewerConfiguration = '$(document).ready(function() {
525
                    if (dlfUtils.exists(dlfViewer)) {
526
                        ' . $jsViewer . '
527
                        viewerCount = ' . ($i - 1) . ';
528
                    }
529
                });';
530
        } else {
531
            $currentMeasureId = '';
532
            $docPage = $this->requestData['page'] ?? 0;
533
534
            $docMeasures = $this->getMeasures($docPage);
535
            if ($this->requestData['measure'] ?? false) {
536
                $currentMeasureId = $docMeasures['measureCounterToMeasureId'][$this->requestData['measure']];
537
            }
538
539
            $viewer = [
540
                'controls' => $this->controls,
541
                'div' => $this->settings['elementId'],
542
                'progressElementId' => $this->settings['progressElementId'] ?? 'tx-dlf-page-progress',
543
                'images' => $this->images,
544
                'fulltexts' => $this->fulltexts,
545
                'score' => $this->scores,
546
                'annotationContainers' => $this->annotationContainers,
547
                'measureCoords' => $docMeasures['measureCoordsCurrentSite'],
548
                'useInternalProxy' => $this->settings['useInternalProxy'],
549
                'verovioAnnotations' => $this->verovioAnnotations,
550
                'currentMeasureId' => $currentMeasureId,
551
                'measureIdLinks' => $docMeasures['measureLinks']
552
            ];
553
554
            // Viewer configuration.
555
            $viewerConfiguration = '$(document).ready(function() {
556
                    if (dlfUtils.exists(dlfViewer)) {
557
                        tx_dlf_viewer = new dlfViewer(' . json_encode($viewer) . ');
558
                    }
559
                });';
560
        }
561
        $this->view->assign('viewerConfiguration', $viewerConfiguration);
562
    }
563
564
    /**
565
     * Get all AnnotationPages / AnnotationLists that contain text Annotations with motivation "painting"
566
     *
567
     * @access protected
568
     *
569
     * @param int $page Page number
570
     * @return array An array containing the IRIs of the AnnotationLists / AnnotationPages as well as some information about the canvas.
571
     */
572
    protected function getAnnotationContainers(int $page): array
573
    {
574
        if ($this->document->getCurrentDocument() instanceof IiifManifest) {
575
            $canvasId = $this->document->getCurrentDocument()->physicalStructure[$page];
576
            $iiif = $this->document->getCurrentDocument()->getIiif();
577
            if ($iiif instanceof ManifestInterface) {
0 ignored issues
show
introduced by
$iiif is always a sub-type of Ubl\Iiif\Presentation\Co...urces\ManifestInterface.
Loading history...
578
                $canvas = $iiif->getContainedResourceById($canvasId);
579
                /* @var $canvas \Ubl\Iiif\Presentation\Common\Model\Resources\CanvasInterface */
580
                if ($canvas != null && !empty($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING))) {
581
                    $annotationContainers = [];
582
                    /*
583
                     *  TODO Analyzing the annotations on the server side requires loading the annotation lists / pages
584
                     *  just to determine whether they contain text annotations for painting. This will take time and lead to a bad user experience.
585
                     *  It would be better to link every annotation and analyze the data on the client side.
586
                     *
587
                     *  On the other hand, server connections are potentially better than client connections. Downloading annotation lists
588
                     */
589
                    foreach ($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING) as $annotationContainer) {
590
                        if (($textAnnotations = $annotationContainer->getTextAnnotations(Motivation::PAINTING)) != null) {
591
                            foreach ($textAnnotations as $annotation) {
592
                                if (
593
                                    $annotation->getBody()->getFormat() == 'text/plain'
594
                                    && $annotation->getBody()->getChars() != null
595
                                ) {
596
                                    $annotationListData = [];
597
                                    $annotationListData['uri'] = $annotationContainer->getId();
598
                                    $annotationListData['label'] = $annotationContainer->getLabelForDisplay();
599
                                    $annotationContainers[] = $annotationListData;
600
                                    break;
601
                                }
602
                            }
603
                        }
604
                    }
605
                    $result = [
606
                        'canvas' => [
607
                            'id' => $canvas->getId(),
608
                            'width' => $canvas->getWidth(),
609
                            'height' => $canvas->getHeight(),
610
                        ],
611
                        'annotationContainers' => $annotationContainers
612
                    ];
613
                    return $result;
614
                }
615
            }
616
        }
617
        return [];
618
    }
619
620
    /**
621
     * Get image's URL and MIME type
622
     *
623
     * @access protected
624
     *
625
     * @param int $page Page number
626
     * @param ?MetsDocument $specificDoc
627
     *
628
     * @return array URL and MIME type of image file
629
     */
630
    protected function getImage(int $page, MetsDocument $specificDoc = null): array
631
    {
632
        $image = [];
633
        // Get @USE value of METS fileGrp.
634
        $fileGrpsImages = GeneralUtility::trimExplode(',', $this->extConf['files']['fileGrpImages']);
635
636
        foreach ($fileGrpsImages as $fileGrpImages) {
637
            // Get file info for the specific page and file group
638
            $file = $this->fetchFileInfo($page, $fileGrpImages, $specificDoc);
639
            if ($file && Helper::filterFilesByMimeType($file, ['image', 'application'], ['IIIF', 'IIP', 'ZOOMIFY'], 'mimeType')) {
640
                $image['url'] = $file['location'];
641
                $image['mimetype'] = $file['mimeType'];
642
643
                // Only deliver static images via the internal PageViewProxy.
644
                // (For IIP and IIIF, the viewer needs to build and access a separate metadata URL, see `getMetadataURL` in `OLSources.js`.)
645
                if ($this->settings['useInternalProxy'] && !Helper::filterFilesByMimeType($file, ['application'], ['IIIF', 'IIP', 'ZOOMIFY'], 'mimeType')) {
646
                    $this->configureProxyUrl($image['url']);
647
                }
648
                break;
649
            } else {
650
                $this->logger->notice('No image file found for page "' . $page . '" in fileGrp "' . $fileGrpImages . '"');
651
            }
652
        }
653
654
        if (empty($image)) {
655
            $this->logger->warning('No image file found for page "' . $page . '" in fileGrps "' . $this->extConf['files']['fileGrpImages'] . '"');
656
        }
657
658
        return $image;
659
    }
660
661
    /**
662
     * Fetch file info for a specific page and file group.
663
     *
664
     * @param int $page Page number
665
     * @param string $fileGrpImages File group
666
     * @param ?MetsDocument $specificDoc Optional specific document
667
     *
668
     * @return array|null File info array or null if not found
669
     */
670
    private function fetchFileInfo(int $page, string $fileGrpImages, ?MetsDocument $specificDoc): ?array
671
    {
672
        // Get the physical structure info for the specified page
673
        if ($specificDoc) {
674
            $physicalStructureInfo = $specificDoc->physicalStructureInfo[$specificDoc->physicalStructure[$page]];
675
        } else {
676
            $physicalStructureInfo = $this->document->getCurrentDocument()->physicalStructureInfo[$this->document->getCurrentDocument()->physicalStructure[$page]];
677
        }
678
679
        // Get the files for the specified file group
680
        $files = $physicalStructureInfo['files'] ?? null;
681
        if ($files && !empty($files[$fileGrpImages])) {
682
            // Get the file info for the specified file group
683
            if ($specificDoc) {
684
                return $specificDoc->getFileInfo($files[$fileGrpImages]);
685
            } else {
686
                return $this->document->getCurrentDocument()->getFileInfo($files[$fileGrpImages]);
687
            }
688
        }
689
690
        return null;
691
    }
692
}
693