Coverage Report

Created: 2026-09-14 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/file.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libheif.
6
 *
7
 * libheif is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libheif 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 Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "file.h"
22
#include "box.h"
23
#include "libheif/heif.h"
24
#include "libheif/heif_properties.h"
25
#include "compression.h"
26
#include "image-items/grid.h"
27
#include "image-items/jpeg2000.h"
28
#include "image-items/jpeg.h"
29
#include "image-items/overlay.h"
30
#include "image-items/vvc.h"
31
#include "codecs/avif_boxes.h"
32
#include "codecs/hevc_boxes.h"
33
#include "sequences/seq_boxes.h"
34
#include "mini.h"
35
36
#include <cstdint>
37
#include <fstream>
38
#include <limits>
39
#include <sstream>
40
#include <utility>
41
#include <cstring>
42
#include <cassert>
43
#include <algorithm>
44
45
#include "libheif/heif_cxx.h"
46
47
#if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER)
48
49
#ifndef NOMINMAX
50
#define NOMINMAX 1
51
#endif
52
53
#include <windows.h>
54
#endif
55
56
57
#if WITH_UNCOMPRESSED_CODEC
58
#include "image-items/unc_image.h"
59
#endif
60
61
// TODO: make this a decoder option
62
104
#define STRICT_PARSING false
63
64
static const char* const too_many_properties_error = "Too many item properties in file to add another one";
65
66
67
HeifFile::HeifFile()
68
26.4k
{
69
26.4k
  m_file_layout = std::make_shared<FileLayout>();
70
26.4k
}
71
72
26.4k
HeifFile::~HeifFile() = default;
73
74
std::vector<heif_item_id> HeifFile::get_item_IDs() const
75
356
{
76
356
  std::vector<heif_item_id> IDs;
77
78
356
  IDs.reserve(m_infe_boxes.size());
79
853
  for (const auto& infe : m_infe_boxes) {
80
853
    IDs.push_back(infe.second->get_item_ID());
81
853
  }
82
83
356
  return IDs;
84
356
}
85
86
87
std::shared_ptr<const Box_infe> HeifFile::get_infe_box(heif_item_id ID) const
88
4.11k
{
89
4.11k
  auto iter = m_infe_boxes.find(ID);
90
4.11k
  if (iter == m_infe_boxes.end()) {
91
0
    return nullptr;
92
0
  }
93
94
4.11k
  return iter->second;
95
4.11k
}
96
97
98
std::shared_ptr<Box_infe> HeifFile::get_infe_box(heif_item_id ID)
99
1.40k
{
100
1.40k
  auto iter = m_infe_boxes.find(ID);
101
1.40k
  if (iter == m_infe_boxes.end()) {
102
0
    return nullptr;
103
0
  }
104
105
1.40k
  return iter->second;
106
1.40k
}
107
108
109
Error HeifFile::read_from_file(const char* input_filename)
110
0
{
111
#if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER)
112
  auto input_stream_istr = std::unique_ptr<std::istream>(new std::ifstream(convert_utf8_path_to_utf16(input_filename).c_str(), std::ios_base::binary));
113
#else
114
0
  auto input_stream_istr = std::unique_ptr<std::istream>(new std::ifstream(input_filename, std::ios_base::binary));
115
0
#endif
116
0
  if (!input_stream_istr->good()) {
117
0
    std::stringstream sstr;
118
0
    sstr << "Error opening file: " << strerror(errno) << " (" << errno << ")\n";
119
0
    return Error(heif_error_Input_does_not_exist, heif_suberror_Unspecified, sstr.str());
120
0
  }
121
122
0
  auto input_stream = std::make_shared<StreamReader_istream>(std::move(input_stream_istr));
123
0
  return read(input_stream);
124
0
}
125
126
127
Error HeifFile::read_from_memory(const void* data, size_t size, bool copy)
128
13.2k
{
129
13.2k
  auto input_stream = std::make_shared<StreamReader_memory>((const uint8_t*) data, size, copy);
130
131
13.2k
  return read(input_stream);
132
13.2k
}
133
134
135
Error HeifFile::read(const std::shared_ptr<StreamReader>& reader)
136
13.2k
{
137
13.2k
  assert(m_limits);
138
139
13.2k
  m_input_stream = reader;
140
141
13.2k
  Error err;
142
13.2k
  err = m_file_layout->read(reader, m_limits);
143
13.2k
  if (err) {
144
7.77k
    return err;
145
7.77k
  }
146
147
5.44k
  Error error = parse_heif_file();
148
5.44k
  if (error) {
149
2.67k
    return error;
150
2.67k
  }
151
152
2.77k
  seed_id_creator();
153
154
2.77k
  return Error::Ok;
155
5.44k
}
156
157
158
bool HeifFile::has_images() const
159
2.80k
{
160
2.80k
  if (!m_meta_box) {
161
2.41k
    return false;
162
2.41k
  }
163
164
385
  return m_hdlr_box && m_hdlr_box->get_handler_type() == fourcc("pict");
165
2.80k
}
166
167
168
void HeifFile::new_empty_file()
169
13.2k
{
170
  //m_input_stream.reset();
171
13.2k
  m_top_level_boxes.clear();
172
173
13.2k
  m_ftyp_box = std::make_shared<Box_ftyp>();
174
13.2k
  m_top_level_boxes.push_back(m_ftyp_box);
175
13.2k
}
176
177
178
void HeifFile::init_for_meta_item()
179
740
{
180
740
  if (!m_meta_box) {
181
0
    m_meta_box = std::make_shared<Box_meta>();
182
0
    m_top_level_boxes.push_back(m_meta_box);
183
0
  }
184
185
740
  if (!m_hdlr_box) {
186
0
    m_hdlr_box = std::make_shared<Box_hdlr>();
187
0
    m_hdlr_box->set_handler_type(fourcc("null"));
188
0
    m_meta_box->append_child_box(m_hdlr_box);
189
0
  }
190
191
740
  if (!m_iloc_box) {
192
356
    m_iloc_box = std::make_shared<Box_iloc>();
193
356
    m_meta_box->append_child_box(m_iloc_box);
194
356
  }
195
196
740
  if (!m_iinf_box) {
197
356
    m_iinf_box = std::make_shared<Box_iinf>();
198
356
    m_meta_box->append_child_box(m_iinf_box);
199
356
  }
200
740
}
201
202
203
void HeifFile::init_for_image()
204
0
{
205
0
  init_for_meta_item();
206
207
  // Upgrade handler from "null" to "pict" if needed
208
0
  if (m_hdlr_box->get_handler_type() == fourcc("null")) {
209
0
    m_hdlr_box->set_handler_type(fourcc("pict"));
210
0
  }
211
212
0
  if (!m_pitm_box) {
213
0
    m_pitm_box = std::make_shared<Box_pitm>();
214
0
    m_meta_box->append_child_box(m_pitm_box);
215
0
  }
216
217
0
  init_for_item_properties();
218
0
}
219
220
221
void HeifFile::init_for_item_properties()
222
740
{
223
740
  init_for_meta_item();
224
225
740
  if (!m_iprp_box) {
226
356
    m_iprp_box = std::make_shared<Box_iprp>();
227
356
    m_meta_box->append_child_box(m_iprp_box);
228
356
  }
229
230
740
  if (!m_ipco_box) {
231
0
    m_ipco_box = std::make_shared<Box_ipco>();
232
0
    m_iprp_box->append_child_box(m_ipco_box);
233
0
  }
234
235
740
  if (!m_ipma_box) {
236
0
    m_ipma_box = std::make_shared<Box_ipma>();
237
0
    m_iprp_box->append_child_box(m_ipma_box);
238
0
  }
239
740
}
240
241
242
void HeifFile::init_for_sequence()
243
0
{
244
0
  if (m_moov_box) {
245
0
    return;
246
0
  }
247
248
0
  m_moov_box = std::make_shared<Box_moov>();
249
0
  m_top_level_boxes.push_back(m_moov_box);
250
251
0
  m_mvhd_box = std::make_shared<Box_mvhd>();
252
0
  m_moov_box->append_child_box(m_mvhd_box);
253
0
}
254
255
256
size_t HeifFile::append_mdat_data(const std::vector<uint8_t>& data)
257
0
{
258
0
  if (!m_mdat_data) {
259
0
    m_mdat_data = std::make_unique<MdatData_Memory>();
260
0
  }
261
262
0
  return m_mdat_data->append_data(data);
263
0
}
264
265
266
void HeifFile::derive_box_versions()
267
0
{
268
0
  for (auto& box : m_top_level_boxes) {
269
0
    box->derive_box_version_recursive();
270
0
  }
271
0
}
272
273
274
void HeifFile::write(StreamWriter& writer)
275
0
{
276
0
  if (m_write_mini_format) {
277
0
    std::string reason;
278
0
    if (Box_mini::can_convert_to_mini(this, reason)) {
279
0
      auto mini = Box_mini::create_from_heif_file(this);
280
0
      if (mini) {
281
        // Adjust ftyp for mini format
282
0
        auto ftyp = get_ftyp_box();
283
284
        // Determine codec brand from primary item type
285
0
        uint32_t item_type = get_item_type_4cc(get_primary_image_ID());
286
0
        heif_brand2 codec_brand = 0;
287
0
        if (item_type == fourcc("av01")) {
288
0
          codec_brand = heif_brand2_avif;
289
0
        }
290
0
        else if (item_type == fourcc("hvc1")) {
291
0
          codec_brand = heif_brand2_heic;
292
0
        }
293
294
0
        ftyp->set_major_brand(fourcc("mif3"));
295
0
        ftyp->set_minor_version(codec_brand);
296
0
        ftyp->clear_compatible_brands();
297
298
        // Write ftyp + mini (no mdat needed)
299
0
        ftyp->write(writer);
300
0
        mini->write(writer);
301
0
        return;
302
0
      }
303
0
    }
304
    // Fall through to normal write if conversion fails
305
0
  }
306
307
0
  for (auto& box : m_top_level_boxes) {
308
0
    if (box == nullptr) {
309
      // Either mini or meta will be null, just ignore that one
310
0
      continue;
311
0
    }
312
0
    Error err = box->write(writer);
313
0
    (void)err; // TODO: error ?
314
0
  }
315
316
0
  if (m_iloc_box) {
317
    // TODO: rewrite to use MdatData class
318
0
    Error err = m_iloc_box->write_mdat_after_iloc(writer);
319
0
    (void)err; // TODO: error ?
320
0
  }
321
322
0
  if (m_mdat_data) {
323
0
    Result<size_t> mdatResult = write_mdat(writer);
324
0
    if (!mdatResult) {
325
0
      return; // TODO: error ?
326
0
    }
327
328
0
    for (auto& box : m_top_level_boxes) {
329
0
      box->patch_file_pointers_recursively(writer, *mdatResult);
330
0
    }
331
0
  }
332
0
}
333
334
335
std::string HeifFile::debug_dump_boxes() const
336
0
{
337
0
  std::stringstream sstr;
338
339
0
  bool first = true;
340
341
0
  for (const auto& box : m_top_level_boxes) {
342
0
    if (box == nullptr) {
343
      // Either mini or meta will be null, just ignore that one
344
0
      continue;
345
0
    }
346
    // dump box content for debugging
347
348
0
    if (first) {
349
0
      first = false;
350
0
    }
351
0
    else {
352
0
      sstr << "\n";
353
0
    }
354
355
0
    Indent indent;
356
0
    sstr << box->dump(indent);
357
0
  }
358
359
0
  return sstr.str();
360
0
}
361
362
363
std::string HeifFile::debug_dump_item_data() const
364
0
{
365
0
  std::stringstream sstr;
366
0
  bool has_output = false;
367
368
0
  sstr << "=== Item Data ===\n";
369
370
0
  for (const auto& [id, infe] : m_infe_boxes) {
371
0
    uint32_t item_type = infe->get_item_type_4cc();
372
373
0
    if (item_type == fourcc("grid")) {
374
0
      auto dataResult = get_uncompressed_item_data(id);
375
0
      if (!dataResult) {
376
0
        continue;
377
0
      }
378
379
0
      ImageGrid grid;
380
0
      Error err = grid.parse(*dataResult);
381
0
      if (err) {
382
0
        continue;
383
0
      }
384
385
0
      has_output = true;
386
0
      sstr << "\nitem ID " << id << " (grid):\n";
387
388
0
      std::istringstream lines(grid.dump());
389
0
      std::string line;
390
0
      while (std::getline(lines, line)) {
391
0
        sstr << "  " << line << "\n";
392
0
      }
393
0
    }
394
0
    else if (item_type == fourcc("iovl")) {
395
0
      if (!m_iref_box) {
396
0
        continue;
397
0
      }
398
399
0
      auto refs = m_iref_box->get_references(id, fourcc("dimg"));
400
401
0
      auto dataResult = get_uncompressed_item_data(id);
402
0
      if (!dataResult) {
403
0
        continue;
404
0
      }
405
406
0
      ImageOverlay overlay;
407
0
      Error err = overlay.parse(refs.size(), *dataResult);
408
0
      if (err) {
409
0
        continue;
410
0
      }
411
412
0
      has_output = true;
413
0
      sstr << "\nitem ID " << id << " (iovl):\n";
414
415
0
      std::istringstream lines(overlay.dump());
416
0
      std::string line;
417
0
      while (std::getline(lines, line)) {
418
0
        sstr << "  " << line << "\n";
419
0
      }
420
0
    }
421
0
  }
422
423
0
  if (has_output) {
424
0
    return sstr.str();
425
0
  }
426
427
0
  return {};
428
0
}
429
430
431
Error HeifFile::parse_heif_file()
432
5.44k
{
433
  // --- read all top-level boxes
434
435
#if 0
436
  for (;;) {
437
    std::shared_ptr<Box> box;
438
    Error error = Box::read(range, &box);
439
440
    if (range.error() || range.eof()) {
441
      break;
442
    }
443
444
    // When an EOF error is returned, this is not really a fatal exception,
445
    // but simply the indication that we reached the end of the file.
446
    // TODO: this design should be cleaned up
447
    if (error.error_code == heif_error_Invalid_input && error.sub_error_code == heif_suberror_End_of_data) {
448
      break;
449
    }
450
451
    if (error != Error::Ok) {
452
      return error;
453
    }
454
455
    m_top_level_boxes.push_back(box);
456
457
458
    // extract relevant boxes (ftyp, meta)
459
460
    if (box->get_short_type() == fourcc("meta")) {
461
      m_meta_box = std::dynamic_pointer_cast<Box_meta>(box);
462
    }
463
464
    if (box->get_short_type() == fourcc("ftyp")) {
465
      m_ftyp_box = std::dynamic_pointer_cast<Box_ftyp>(box);
466
    }
467
  }
468
#endif
469
470
5.44k
  m_ftyp_box = m_file_layout->get_ftyp_box();
471
5.44k
  if (!m_ftyp_box) {
472
0
    return Error(heif_error_Invalid_input,
473
0
                 heif_suberror_No_ftyp_box);
474
0
  }
475
476
5.44k
  m_top_level_boxes.push_back(m_ftyp_box);
477
478
5.44k
  bool is_sequence_brand = (m_ftyp_box->has_compatible_brand(heif_brand2_msf1) ||
479
4.02k
                            m_ftyp_box->has_compatible_brand(heif_brand2_isom) ||
480
2.62k
                            m_ftyp_box->has_compatible_brand(heif_brand2_mp41) ||
481
2.62k
                            m_ftyp_box->has_compatible_brand(heif_brand2_mp42));
482
483
  // --- check whether this is a HEIF file and its structural format
484
485
5.44k
  if (!m_ftyp_box->has_compatible_brand(heif_brand2_heic) &&
486
5.44k
      !m_ftyp_box->has_compatible_brand(heif_brand2_heix) &&
487
5.44k
      !m_ftyp_box->has_compatible_brand(heif_brand2_mif1) &&
488
5.44k
      !m_ftyp_box->has_compatible_brand(heif_brand2_avif) &&
489
5.43k
      !m_ftyp_box->has_compatible_brand(heif_brand2_1pic) &&
490
5.43k
      !(m_ftyp_box->get_major_brand() == heif_brand2_mif3) &&
491
5.20k
      !m_ftyp_box->has_compatible_brand(heif_brand2_jpeg) &&
492
5.19k
      !m_ftyp_box->has_compatible_brand(heif_brand2_isom) &&
493
3.34k
      !m_ftyp_box->has_compatible_brand(heif_brand2_mp42) &&
494
3.34k
      !m_ftyp_box->has_compatible_brand(heif_brand2_mp41) &&
495
3.34k
      !m_ftyp_box->has_compatible_brand(heif_brand2_msf1)) {
496
2.38k
    std::stringstream sstr;
497
2.38k
    sstr << "File does not include any supported brands.\n";
498
499
2.38k
    return Error(heif_error_Unsupported_filetype,
500
2.38k
                 heif_suberror_Unspecified,
501
2.38k
                 sstr.str());
502
2.38k
      }
503
504
3.06k
  m_mini_box = m_file_layout->get_mini_box();
505
506
3.06k
  if (m_mini_box) {
507
480
    m_top_level_boxes.push_back(m_mini_box);
508
509
480
    Error err = m_mini_box->create_expanded_boxes(this);
510
480
    if (err) {
511
124
      return err;
512
124
    }
513
356
    return Error::Ok;
514
480
  }
515
516
2.58k
  m_meta_box = m_file_layout->get_meta_box();
517
2.58k
  if (m_meta_box) {
518
53
    m_top_level_boxes.push_back(m_meta_box);
519
53
  }
520
521
  // TODO: we are missing 'mdat' top level boxes
522
523
2.58k
  m_moov_box = m_file_layout->get_moov_box();
524
2.58k
  if (m_moov_box) {
525
2.53k
    m_top_level_boxes.push_back(m_moov_box);
526
2.53k
  }
527
528
  // if we didn't find the mini box, meta is required
529
530
2.58k
  if (!m_meta_box && !is_sequence_brand) {
531
15
    return Error(heif_error_Invalid_input,
532
15
                 heif_suberror_No_meta_box);
533
15
  }
534
535
2.57k
  if (!m_moov_box && is_sequence_brand) {
536
1
    return Error(heif_error_Invalid_input,
537
1
                 heif_suberror_No_moov_box);
538
1
  }
539
540
2.57k
  if (m_meta_box) {
541
52
    auto err = parse_heif_images();
542
52
    if (err) {
543
48
      return err;
544
48
    }
545
52
  }
546
547
2.52k
  if (m_moov_box) {
548
2.52k
    auto err = parse_heif_sequences();
549
2.52k
    if (err) {
550
102
      return err;
551
102
    }
552
2.52k
  }
553
554
2.42k
  return Error::Ok;
555
2.52k
}
556
557
558
Error HeifFile::parse_heif_images()
559
52
{
560
52
  m_hdlr_box = m_meta_box->get_child_box<Box_hdlr>();
561
52
  if (STRICT_PARSING && !m_hdlr_box) {
562
0
    return Error(heif_error_Invalid_input,
563
0
                 heif_suberror_No_hdlr_box);
564
0
  }
565
566
  // --- assign item boxes
567
568
52
  m_iinf_box = m_meta_box->get_child_box<Box_iinf>();
569
52
  if (!m_iinf_box) {
570
26
    return Error(heif_error_Invalid_input,
571
26
                 heif_suberror_No_iinf_box);
572
26
  }
573
574
26
  std::vector<std::shared_ptr<Box_infe>> infe_boxes = m_iinf_box->get_child_boxes<Box_infe>();
575
576
26
  for (auto& infe_box : infe_boxes) {
577
0
    if (!infe_box) {
578
0
      return Error(heif_error_Invalid_input,
579
0
                   heif_suberror_No_infe_box);
580
0
    }
581
582
0
    m_infe_boxes.insert(std::make_pair(infe_box->get_item_ID(), infe_box));
583
0
  }
584
585
586
26
  if (has_images()) {
587
    // --- find mandatory boxes needed for image decoding
588
589
6
    m_pitm_box = m_meta_box->get_child_box<Box_pitm>();
590
6
    if (!m_pitm_box) {
591
6
      return Error(heif_error_Invalid_input,
592
6
                   heif_suberror_No_pitm_box);
593
6
    }
594
595
0
    m_iprp_box = m_meta_box->get_child_box<Box_iprp>();
596
0
    if (!m_iprp_box) {
597
0
      return Error(heif_error_Invalid_input,
598
0
                   heif_suberror_No_iprp_box);
599
0
    }
600
601
0
    m_ipco_box = m_iprp_box->get_child_box<Box_ipco>();
602
0
    if (!m_ipco_box) {
603
0
      return Error(heif_error_Invalid_input,
604
0
                   heif_suberror_No_ipco_box);
605
0
    }
606
607
0
    auto ipma_boxes = m_iprp_box->get_child_boxes<Box_ipma>();
608
0
    if (ipma_boxes.empty()) {
609
0
      return Error(heif_error_Invalid_input,
610
0
                   heif_suberror_No_ipma_box);
611
0
    }
612
0
    for (size_t i=1;i<ipma_boxes.size();i++) {
613
0
      ipma_boxes[0]->insert_entries_from_other_ipma_box(*ipma_boxes[i]);
614
0
    }
615
0
    m_ipma_box = ipma_boxes[0];
616
0
  }
617
618
20
  m_iloc_box = m_meta_box->get_child_box<Box_iloc>();
619
20
  if (!m_iloc_box) {
620
16
    return Error(heif_error_Invalid_input,
621
16
                 heif_suberror_No_iloc_box);
622
16
  }
623
624
4
  m_idat_box = m_meta_box->get_child_box<Box_idat>();
625
626
4
  m_iref_box = m_meta_box->get_child_box<Box_iref>();
627
628
  // Note: reference cycles are not rejected at load. A cycle only matters when
629
  // it is decoded, and it is caught there per item by ImageItem::verify_decodable()
630
  // (following both 'dimg' and 'auxl' edges). Rejecting the whole file at load
631
  // would also make its unaffected, independently valid items undecodable.
632
633
4
  m_grpl_box = m_meta_box->get_child_box<Box_grpl>();
634
635
636
4
  return Error::Ok;
637
20
}
638
639
640
Error HeifFile::parse_heif_sequences()
641
2.52k
{
642
2.52k
  m_mvhd_box = m_moov_box->get_child_box<Box_mvhd>();
643
2.52k
  if (!m_mvhd_box) {
644
102
    return {heif_error_Invalid_input,
645
102
            heif_suberror_Unspecified,
646
102
            "No mvhd box in image sequence."};
647
102
  }
648
649
2.41k
  return Error::Ok;
650
2.52k
}
651
652
653
bool HeifFile::item_exists(heif_item_id ID) const
654
549
{
655
549
  auto image_iter = m_infe_boxes.find(ID);
656
549
  return image_iter != m_infe_boxes.end();
657
549
}
658
659
660
bool HeifFile::has_item_with_id(heif_item_id ID) const
661
0
{
662
0
  auto infe_box = get_infe_box(ID);
663
0
  return infe_box != nullptr;
664
0
}
665
666
667
uint32_t HeifFile::get_item_type_4cc(heif_item_id ID) const
668
2.36k
{
669
2.36k
  auto infe_box = get_infe_box(ID);
670
2.36k
  if (!infe_box) {
671
0
    return 0;
672
0
  }
673
674
2.36k
  return infe_box->get_item_type_4cc();
675
2.36k
}
676
677
678
std::string HeifFile::get_content_type(heif_item_id ID) const
679
853
{
680
853
  auto infe_box = get_infe_box(ID);
681
853
  if (!infe_box) {
682
0
    return "";
683
0
  }
684
685
853
  return infe_box->get_content_type();
686
853
}
687
688
std::string HeifFile::get_item_uri_type(heif_item_id ID) const
689
347
{
690
347
  auto infe_box = get_infe_box(ID);
691
347
  if (!infe_box) {
692
0
    return "";
693
0
  }
694
695
347
  return infe_box->get_item_uri_type();
696
347
}
697
698
699
Error HeifFile::get_properties(heif_item_id imageID,
700
                               std::vector<std::shared_ptr<Box>>& properties) const
701
1.06k
{
702
1.06k
  if (!m_ipco_box) {
703
0
    return Error(heif_error_Invalid_input,
704
0
                 heif_suberror_No_ipco_box);
705
0
  }
706
1.06k
  else if (!m_ipma_box) {
707
0
    return Error(heif_error_Invalid_input,
708
0
                 heif_suberror_No_ipma_box);
709
0
  }
710
711
1.06k
  return m_ipco_box->get_properties_for_item_ID(imageID, m_ipma_box, properties);
712
1.06k
}
713
714
715
Result<std::vector<uint8_t>> HeifFile::get_uncompressed_item_data(heif_item_id ID) const
716
549
{
717
549
  assert(m_limits);
718
719
549
#if ENABLE_PARALLEL_TILE_DECODING
720
  // std::lock_guard<std::mutex> guard(m_read_mutex);   // TODO: I think that this is not needed anymore because this function is not used for image data anymore.
721
549
#endif
722
723
549
  if (!m_iloc_box) {
724
0
    return Error(heif_error_Invalid_input, heif_suberror_No_iloc_box);
725
0
  }
726
727
549
  if (!item_exists(ID)) {
728
0
    return Error(heif_error_Usage_error,
729
0
                 heif_suberror_Nonexisting_item_referenced);
730
0
  }
731
732
549
  auto infe_box = get_infe_box(ID);
733
549
  if (!infe_box) {
734
0
    return Error(heif_error_Usage_error,
735
0
                 heif_suberror_Nonexisting_item_referenced);
736
0
  }
737
738
739
549
  uint32_t item_type = infe_box->get_item_type_4cc();
740
549
  std::string content_type = infe_box->get_content_type();
741
742
  // --- decompress data
743
744
549
  Error error;
745
746
549
  if (item_type == fourcc("mime")) {
747
436
    std::string encoding = infe_box->get_content_encoding();
748
    // Skip the encoding comparisons when no encoding is set; falling through
749
    // to the raw read below is the right behaviour for the uncompressed case.
750
    // Skipping is also necessary to avoid libstdc++'s _S_compare(0, N) which
751
    // computes unsigned `0 - N` and trips UBSan even though the cast result
752
    // is benign.
753
436
    if (!encoding.empty() && encoding != "identity") {
754
152
      if (encoding == "compress_zlib") {
755
0
#if HAVE_ZLIB
756
0
        std::vector<uint8_t> compressed_data;
757
0
        error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
758
0
        if (error) {
759
0
          return error;
760
0
        }
761
762
0
        return decompress_zlib(compressed_data, m_limits);
763
#else
764
        return Error(heif_error_Unsupported_feature,
765
                     heif_suberror_Unsupported_header_compression_method,
766
                     encoding);
767
#endif
768
0
      }
769
152
      else if (encoding == "deflate") {
770
152
#if HAVE_ZLIB
771
152
        std::vector<uint8_t> compressed_data;
772
152
        error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
773
152
        if (error) {
774
0
          return error;
775
0
        }
776
152
        return decompress_deflate(compressed_data, m_limits);
777
#else
778
        return Error(heif_error_Unsupported_feature,
779
                     heif_suberror_Unsupported_header_compression_method,
780
                     encoding);
781
#endif
782
152
      }
783
0
      else if (encoding == "br") {
784
0
#if HAVE_BROTLI
785
0
        std::vector<uint8_t> compressed_data;
786
0
        error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
787
0
        if (error) {
788
0
          return error;
789
0
        }
790
0
        return decompress_brotli(compressed_data, m_limits);
791
#else
792
        return Error(heif_error_Unsupported_feature,
793
                     heif_suberror_Unsupported_header_compression_method,
794
                     encoding);
795
#endif
796
0
      }
797
0
      else {
798
0
        return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_codec);
799
0
      }
800
152
    }
801
436
  }
802
803
804
  // --- read uncompressed
805
806
397
  std::vector<uint8_t> data;
807
397
  error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &data, m_limits);
808
397
  if (error) {
809
0
    return error;
810
0
  }
811
397
  else {
812
397
    return data;
813
397
  }
814
397
}
815
816
817
Error HeifFile::append_data_from_file_range(std::vector<uint8_t>& out_data, uint64_t offset, uint32_t size) const
818
27.0k
{
819
27.0k
  auto old_size = out_data.size();
820
821
  // --- check that the requested range does not exceed the file size
822
823
27.0k
  uint64_t end_pos = offset + size;
824
27.0k
  if (m_input_stream->wait_for_file_size(end_pos) != StreamReader::grow_status::size_reached) {
825
96
    std::stringstream sstr;
826
96
    sstr << "File range " << offset << ".." << end_pos << " is beyond end of file.";
827
96
    return {heif_error_Invalid_input,
828
96
            heif_suberror_End_of_data,
829
96
            sstr.str()};
830
96
  }
831
832
  // --- check security limit on resulting buffer size
833
834
26.9k
  if (m_limits) {
835
26.9k
    auto max_memory_block_size = m_limits->max_memory_block_size;
836
26.9k
    if (max_memory_block_size && max_memory_block_size - old_size < size) {
837
0
      std::stringstream sstr;
838
0
      sstr << "Sample data of " << size << " bytes would grow total buffer to "
839
0
           << (old_size + size) << " bytes, exceeding the security limit of "
840
0
           << max_memory_block_size << " bytes.";
841
0
      return {heif_error_Memory_allocation_error,
842
0
              heif_suberror_Security_limit_exceeded,
843
0
              sstr.str()};
844
0
    }
845
26.9k
  }
846
847
26.9k
  if (!m_input_stream->seek(offset)) {
848
0
    return {heif_error_Invalid_input,
849
0
            heif_suberror_End_of_data,
850
0
            "Cannot seek to sample data offset."};
851
0
  }
852
853
26.9k
  out_data.resize(old_size + size);
854
855
26.9k
  if (!m_input_stream->read(out_data.data() + old_size, size)) {
856
0
    out_data.resize(old_size);
857
0
    return {heif_error_Invalid_input,
858
0
            heif_suberror_End_of_data,
859
0
            "Failed to read sample data from file."};
860
0
  }
861
862
26.9k
  return {};
863
26.9k
}
864
865
866
Error HeifFile::append_data_from_iloc(heif_item_id ID, std::vector<uint8_t>& out_data, uint64_t offset, uint64_t size) const
867
0
{
868
0
  if (!m_iloc_box) {
869
0
    return Error(heif_error_Invalid_input, heif_suberror_No_iloc_box);
870
0
  }
871
872
0
  const auto& items = m_iloc_box->get_items();
873
0
  const Box_iloc::Item* item = nullptr;
874
0
  for (const auto& i : items) {
875
0
    if (i.item_ID == ID) {
876
0
      item = &i;
877
0
      break;
878
0
    }
879
0
  }
880
0
  if (!item) {
881
0
    std::stringstream sstr;
882
0
    sstr << "Item with ID " << ID << " has no compressed data";
883
884
0
    return {heif_error_Invalid_input,
885
0
            heif_suberror_No_item_data,
886
0
            sstr.str()};
887
0
  }
888
889
0
  return m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &out_data, offset, size, m_limits);
890
0
}
891
892
893
Result<std::vector<uint8_t>> HeifFile::get_item_data(heif_item_id ID, heif_metadata_compression* out_compression) const
894
0
{
895
0
  Error error;
896
897
0
  assert(m_limits);
898
899
0
  if (!m_iloc_box) {
900
0
    return Error(heif_error_Invalid_input, heif_suberror_No_iloc_box);
901
0
  }
902
903
0
  auto infe_box = get_infe_box(ID);
904
0
  if (!infe_box) {
905
0
    return Error{heif_error_Usage_error,
906
0
                 heif_suberror_Nonexisting_item_referenced};
907
0
  }
908
909
0
  uint32_t item_type = infe_box->get_item_type_4cc();
910
0
  std::string content_type = infe_box->get_content_type();
911
912
  // --- non 'mime' data (uncompressed)
913
914
0
  if (item_type != fourcc("mime")) {
915
0
    if (out_compression) {
916
0
      *out_compression = heif_metadata_compression_off;
917
0
    }
918
919
0
    std::vector<uint8_t> out_data;
920
0
    Error err = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &out_data, m_limits);
921
0
    if (err) {
922
0
      return err;
923
0
    }
924
0
    else {
925
0
      return out_data;
926
0
    }
927
0
  }
928
929
930
  // --- mime data
931
932
0
  std::string encoding = infe_box->get_content_encoding();
933
934
0
  heif_metadata_compression compression;
935
936
0
  if (encoding.empty() || encoding == "identity") {
937
    // shortcut for case of uncompressed mime data ("identity" is the RFC 2616 no-op coding)
938
939
0
    if (out_compression) {
940
0
      *out_compression = heif_metadata_compression_off;
941
0
    }
942
943
0
    std::vector<uint8_t> out_data;
944
0
    Error err = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &out_data, m_limits);
945
0
    if (err) {
946
0
      return err;
947
0
    }
948
0
    else {
949
0
      return out_data;
950
0
    }
951
0
  }
952
0
  else if (encoding == "compress_zlib") {
953
0
    compression = heif_metadata_compression_zlib;
954
0
  }
955
0
  else if (encoding == "deflate") {
956
0
    compression = heif_metadata_compression_deflate;
957
0
  }
958
0
  else if (encoding == "br") {
959
0
    compression = heif_metadata_compression_brotli;
960
0
  }
961
0
  else {
962
0
    compression = heif_metadata_compression_unknown;
963
0
  }
964
965
  // read compressed data
966
967
0
  std::vector<uint8_t> compressed_data;
968
0
  error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
969
0
  if (error) {
970
0
    return error;
971
0
  }
972
973
  // return compressed data, if we do not want to have it uncompressed
974
975
0
  const bool return_compressed = (out_compression != nullptr);
976
0
  if (return_compressed) {
977
0
    *out_compression = compression;
978
0
    return compressed_data;
979
0
  }
980
981
  // decompress the data
982
983
0
  switch (compression) {
984
0
#if HAVE_ZLIB
985
0
    case heif_metadata_compression_zlib:
986
0
      return decompress_zlib(compressed_data, m_limits);
987
0
    case heif_metadata_compression_deflate:
988
0
      return decompress_deflate(compressed_data, m_limits);
989
0
#endif
990
0
#if HAVE_BROTLI
991
0
    case heif_metadata_compression_brotli:
992
0
      return decompress_brotli(compressed_data, m_limits);
993
0
#endif
994
0
    default:
995
0
      return Error{heif_error_Unsupported_filetype, heif_suberror_Unsupported_header_compression_method};
996
0
  }
997
0
}
998
999
1000
Result<heif_item_id> HeifFile::get_unused_item_id()
1001
0
{
1002
0
  return m_id_creator.get_new_id(IDCreator::Namespace::item);
1003
0
}
1004
1005
1006
void HeifFile::seed_id_creator()
1007
2.77k
{
1008
  // A file with the 'unif' brand uses one ID space for items, tracks and entity groups.
1009
  // Keep it that way for everything we add to the file.
1010
2.77k
  if (m_ftyp_box && m_ftyp_box->has_compatible_brand(heif_brand2_unif)) {
1011
0
    m_id_creator.set_unif(true);
1012
0
  }
1013
1014
2.77k
  for (const auto& infe : m_infe_boxes) {
1015
853
    m_id_creator.mark_id_used(IDCreator::Namespace::item, infe.first);
1016
853
  }
1017
1018
2.77k
  if (m_grpl_box) {
1019
0
    for (const auto& box : m_grpl_box->get_all_child_boxes()) {
1020
0
      if (auto group = std::dynamic_pointer_cast<Box_EntityToGroup>(box)) {
1021
0
        m_id_creator.mark_id_used(IDCreator::Namespace::entity_group, group->get_group_id());
1022
0
      }
1023
0
    }
1024
0
  }
1025
1026
2.77k
  if (m_moov_box) {
1027
2.54k
    for (const auto& trak : m_moov_box->get_child_boxes<Box_trak>()) {
1028
2.54k
      if (auto tkhd = trak->get_child_box<Box_tkhd>()) {
1029
2.49k
        m_id_creator.mark_id_used(IDCreator::Namespace::track, tkhd->get_track_id());
1030
2.49k
      }
1031
2.54k
    }
1032
2.41k
  }
1033
2.77k
}
1034
1035
1036
void HeifFile::remove_infe_box(const std::shared_ptr<Box_infe>& infe)
1037
0
{
1038
0
  m_infe_boxes.erase(infe->get_item_ID());
1039
1040
0
  if (m_iinf_box) {
1041
0
    m_iinf_box->remove_child_box(infe);
1042
0
  }
1043
0
}
1044
1045
1046
Result<heif_item_id> HeifFile::add_new_image(uint32_t item_type)
1047
0
{
1048
0
  auto result = add_new_infe_box(item_type);
1049
0
  if (!result) {
1050
0
    return result.error();
1051
0
  }
1052
0
  return (*result)->get_item_ID();
1053
0
}
1054
1055
1056
Result<std::shared_ptr<Box_infe>> HeifFile::add_new_infe_box(uint32_t item_type)
1057
0
{
1058
0
  init_for_image();
1059
1060
0
  auto idResult = get_unused_item_id();
1061
0
  if (!idResult) {
1062
0
    return idResult.error();
1063
0
  }
1064
0
  heif_item_id id = *idResult;
1065
1066
0
  auto infe = std::make_shared<Box_infe>();
1067
0
  infe->set_item_ID(id);
1068
0
  infe->set_hidden_item(false);
1069
0
  infe->set_item_type_4cc(item_type);
1070
1071
0
  m_infe_boxes[id] = infe;
1072
0
  m_iinf_box->append_child_box(infe);
1073
1074
0
  return infe;
1075
0
}
1076
1077
1078
Result<std::shared_ptr<Box_infe>> HeifFile::add_new_meta_infe_box(uint32_t item_type)
1079
0
{
1080
0
  init_for_meta_item();
1081
1082
0
  auto idResult = get_unused_item_id();
1083
0
  if (!idResult) {
1084
0
    return idResult.error();
1085
0
  }
1086
0
  heif_item_id id = *idResult;
1087
1088
0
  auto infe = std::make_shared<Box_infe>();
1089
0
  infe->set_item_ID(id);
1090
0
  infe->set_hidden_item(false);
1091
0
  infe->set_item_type_4cc(item_type);
1092
1093
0
  m_infe_boxes[id] = infe;
1094
0
  m_iinf_box->append_child_box(infe);
1095
1096
0
  return infe;
1097
0
}
1098
1099
1100
Error HeifFile::add_ispe_property(heif_item_id id, uint32_t width, uint32_t height, bool essential)
1101
0
{
1102
0
  init_for_item_properties();
1103
1104
0
  auto ispe = std::make_shared<Box_ispe>();
1105
0
  ispe->set_size(width, height);
1106
1107
0
  uint32_t index = m_ipco_box->find_or_append_child_box(ispe);
1108
1109
  // 'ipma' stores the property index as a 16 bit value. Rather than truncating it and writing a
1110
  // wrong association, refuse to add the property.
1111
0
  if (index + 1 > 0xFFFF) {
1112
0
    return {heif_error_Encoding_error,
1113
0
            heif_suberror_Unspecified,
1114
0
            too_many_properties_error};
1115
0
  }
1116
1117
0
  m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{essential, uint16_t(index + 1)});
1118
1119
0
  return Error::Ok;
1120
0
}
1121
1122
1123
1124
heif_property_id HeifFile::add_property(heif_item_id id, const std::shared_ptr<Box>& property, bool essential)
1125
234
{
1126
234
  init_for_item_properties();
1127
1128
234
  uint32_t index = m_ipco_box->find_or_append_child_box(property);
1129
1130
  // 'ipma' stores the property index as a 16 bit value. Rather than truncating it and writing a
1131
  // wrong association, refuse to add the property.
1132
234
  if (index + 1 > 0xFFFF) {
1133
0
    return 0;
1134
0
  }
1135
1136
  // Note that we have to return the position within the item's property list and not 'index',
1137
  // as 'index' is the position in the file-wide 'ipco' box, which is shared by all items.
1138
234
  return m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{essential, uint16_t(index + 1)});
1139
234
}
1140
1141
1142
heif_property_id HeifFile::add_property_without_deduplication(heif_item_id id, const std::shared_ptr<Box>& property, bool essential)
1143
0
{
1144
0
  init_for_item_properties();
1145
1146
0
  uint32_t index = m_ipco_box->append_child_box(property);
1147
0
  if (index + 1 > 0xFFFF) {
1148
0
    return 0;
1149
0
  }
1150
1151
0
  return m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{essential, uint16_t(index + 1)});
1152
0
}
1153
1154
1155
Error HeifFile::add_orientation_properties(heif_item_id id, heif_orientation orientation)
1156
506
{
1157
506
  init_for_item_properties();
1158
1159
  // Note: ISO/IEC 23000-22:2019(E) (MIAF) 7.3.6.7 requires the following order:
1160
  // clean aperture first, then rotation, then mirror
1161
1162
506
  int rotation_ccw = 0;
1163
506
  heif_transform_mirror_direction mirror;
1164
506
  bool has_mirror = false;
1165
1166
506
  switch (orientation) {
1167
58
    case heif_orientation_normal:
1168
58
      break;
1169
42
    case heif_orientation_flip_horizontally:
1170
42
      mirror = heif_transform_mirror_direction_horizontal;
1171
42
      has_mirror = true;
1172
42
      break;
1173
31
    case heif_orientation_rotate_180:
1174
31
      rotation_ccw = 180;
1175
31
      break;
1176
94
    case heif_orientation_flip_vertically:
1177
94
      mirror = heif_transform_mirror_direction_vertical;
1178
94
      has_mirror = true;
1179
94
      break;
1180
98
    case heif_orientation_rotate_90_cw_then_flip_horizontally:
1181
98
      rotation_ccw = 270;
1182
98
      mirror = heif_transform_mirror_direction_horizontal;
1183
98
      has_mirror = true;
1184
98
      break;
1185
22
    case heif_orientation_rotate_90_cw:
1186
22
      rotation_ccw = 270;
1187
22
      break;
1188
111
    case heif_orientation_rotate_90_cw_then_flip_vertically:
1189
111
      rotation_ccw = 270;
1190
111
      mirror = heif_transform_mirror_direction_vertical;
1191
111
      has_mirror = true;
1192
111
      break;
1193
50
    case heif_orientation_rotate_270_cw:
1194
50
      rotation_ccw = 90;
1195
50
      break;
1196
506
  }
1197
1198
  // omit rotation when angle is 0
1199
506
  if (rotation_ccw!=0) {
1200
312
    auto irot = std::make_shared<Box_irot>();
1201
312
    irot->set_rotation_ccw(rotation_ccw);
1202
1203
312
    uint32_t index = m_ipco_box->find_or_append_child_box(irot);
1204
312
    if (index + 1 > 0xFFFF) {
1205
0
      return {heif_error_Encoding_error, heif_suberror_Unspecified, too_many_properties_error};
1206
0
    }
1207
1208
312
    m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
1209
312
  }
1210
1211
506
  if (has_mirror) {
1212
345
    auto imir = std::make_shared<Box_imir>();
1213
345
    imir->set_mirror_direction(mirror);
1214
1215
345
    uint32_t index = m_ipco_box->find_or_append_child_box(imir);
1216
345
    if (index + 1 > 0xFFFF) {
1217
0
      return {heif_error_Encoding_error, heif_suberror_Unspecified, too_many_properties_error};
1218
0
    }
1219
1220
345
    m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
1221
345
  }
1222
1223
506
  return Error::Ok;
1224
506
}
1225
1226
1227
Result<heif_item_id> HeifFile::add_infe(uint32_t item_type, const uint8_t* data, size_t size)
1228
0
{
1229
  // create an infe box describing what kind of data we are storing (this also creates a new ID)
1230
1231
0
  auto infe_result = add_new_meta_infe_box(item_type);
1232
0
  if (!infe_result) {
1233
0
    return infe_result.error();
1234
0
  }
1235
0
  auto infe_box = *infe_result;
1236
0
  infe_box->set_hidden_item(true);
1237
1238
0
  heif_item_id metadata_id = infe_box->get_item_ID();
1239
1240
0
  if (Error err = set_item_data(infe_box, data, size, heif_metadata_compression_off)) {
1241
0
    remove_infe_box(infe_box);
1242
0
    return err;
1243
0
  }
1244
1245
0
  return metadata_id;
1246
0
}
1247
1248
1249
void HeifFile::add_infe_box(heif_item_id id, std::shared_ptr<Box_infe> infe)
1250
885
{
1251
885
  m_infe_boxes.insert(std::make_pair(id, std::move(infe)));
1252
885
}
1253
1254
1255
Result<heif_item_id> HeifFile::add_infe_mime(const char* content_type, heif_metadata_compression content_encoding, const uint8_t* data, size_t size)
1256
0
{
1257
  // create an infe box describing what kind of data we are storing (this also creates a new ID)
1258
1259
0
  auto infe_result = add_new_meta_infe_box(fourcc("mime"));
1260
0
  if (!infe_result) {
1261
0
    return infe_result.error();
1262
0
  }
1263
0
  auto infe_box = *infe_result;
1264
0
  infe_box->set_hidden_item(true);
1265
0
  infe_box->set_content_type(content_type);
1266
1267
0
  heif_item_id metadata_id = infe_box->get_item_ID();
1268
1269
0
  if (Error err = set_item_data(infe_box, data, size, content_encoding)) {
1270
0
    remove_infe_box(infe_box);
1271
0
    return err;
1272
0
  }
1273
1274
0
  return metadata_id;
1275
0
}
1276
1277
1278
Result<heif_item_id> HeifFile::add_precompressed_infe_mime(const char* content_type, const std::string& content_encoding, const uint8_t* data, size_t size)
1279
0
{
1280
  // create an infe box describing what kind of data we are storing (this also creates a new ID)
1281
1282
0
  auto infe_result = add_new_meta_infe_box(fourcc("mime"));
1283
0
  if (!infe_result) {
1284
0
    return infe_result.error();
1285
0
  }
1286
0
  auto infe_box = *infe_result;
1287
0
  infe_box->set_hidden_item(true);
1288
0
  infe_box->set_content_type(content_type);
1289
1290
0
  heif_item_id metadata_id = infe_box->get_item_ID();
1291
1292
0
  if (Error err = set_precompressed_item_data(infe_box, data, size, content_encoding)) {
1293
0
    remove_infe_box(infe_box);
1294
0
    return err;
1295
0
  }
1296
1297
0
  return metadata_id;
1298
0
}
1299
1300
1301
Result<heif_item_id> HeifFile::add_infe_uri(const char* item_uri_type, const uint8_t* data, size_t size)
1302
0
{
1303
  // create an infe box describing what kind of data we are storing (this also creates a new ID)
1304
1305
0
  auto infe_result = add_new_meta_infe_box(fourcc("uri "));
1306
0
  if (!infe_result) {
1307
0
    return infe_result.error();
1308
0
  }
1309
0
  auto infe_box = *infe_result;
1310
0
  infe_box->set_hidden_item(true);
1311
0
  infe_box->set_item_uri_type(item_uri_type);
1312
1313
0
  heif_item_id metadata_id = infe_box->get_item_ID();
1314
1315
0
  if (Error err = set_item_data(infe_box, data, size, heif_metadata_compression_off)) {
1316
0
    remove_infe_box(infe_box);
1317
0
    return err;
1318
0
  }
1319
1320
0
  return metadata_id;
1321
0
}
1322
1323
1324
Error HeifFile::set_item_data(const std::shared_ptr<Box_infe>& item, const uint8_t* data, size_t size, heif_metadata_compression compression)
1325
0
{
1326
  // --- metadata compression
1327
1328
0
  if (compression == heif_metadata_compression_auto) {
1329
0
    compression = heif_metadata_compression_off; // currently, we don't use header compression by default
1330
0
  }
1331
1332
  // only set metadata compression for MIME type data which has 'content_encoding' field
1333
0
  if (compression != heif_metadata_compression_off &&
1334
0
      item->get_item_type_4cc() != fourcc("mime")) {
1335
0
    return Error(heif_error_Usage_error,
1336
0
                 heif_suberror_Invalid_parameter_value,
1337
0
                 "Item data compression is only supported for 'mime' items");
1338
0
  }
1339
1340
1341
0
  std::vector<uint8_t> data_array;
1342
0
  if (compression == heif_metadata_compression_zlib) {
1343
0
#if HAVE_ZLIB
1344
0
    data_array = compress_zlib((const uint8_t*) data, size);
1345
0
    item->set_content_encoding("compress_zlib");
1346
#else
1347
    return Error(heif_error_Unsupported_feature,
1348
                 heif_suberror_Unsupported_header_compression_method);
1349
#endif
1350
0
  }
1351
0
  else if (compression == heif_metadata_compression_deflate) {
1352
0
#if HAVE_ZLIB
1353
0
    data_array = compress_deflate((const uint8_t*) data, size);
1354
0
    item->set_content_encoding("deflate");
1355
#else
1356
    return Error(heif_error_Unsupported_feature,
1357
                 heif_suberror_Unsupported_header_compression_method);
1358
#endif
1359
0
  }
1360
0
  else if (compression == heif_metadata_compression_brotli) {
1361
0
#if HAVE_BROTLI
1362
0
    data_array = compress_brotli((const uint8_t*)data, size);
1363
0
    item->set_content_encoding("br");
1364
#else
1365
    return Error(heif_error_Unsupported_feature,
1366
                 heif_suberror_Unsupported_header_compression_method);
1367
#endif
1368
0
  }
1369
0
  else {
1370
    // uncompressed data, plain copy
1371
1372
0
    if (size > 0) { // Note: this 'if' is not necessary, but a workaround to a compiler bug (https://github.com/strukturag/libheif/issues/1421)
1373
0
      data_array.resize(size);
1374
0
      memcpy(data_array.data(), data, size);
1375
0
    }
1376
0
  }
1377
1378
  // copy the data into the file, store the pointer to it in an iloc box entry
1379
1380
0
  append_iloc_data(item->get_item_ID(), data_array, 0);
1381
1382
0
  return Error::Ok;
1383
0
}
1384
1385
1386
Error HeifFile::set_precompressed_item_data(const std::shared_ptr<Box_infe>& item, const uint8_t* data, size_t size, const std::string& content_encoding)
1387
0
{
1388
  // only set metadata compression for MIME type data which has 'content_encoding' field
1389
0
  if (!content_encoding.empty() &&
1390
0
      item->get_item_type_4cc() != fourcc("mime")) {
1391
0
    return Error(heif_error_Usage_error,
1392
0
                 heif_suberror_Invalid_parameter_value,
1393
0
                 "A content_encoding can only be set for 'mime' items");
1394
0
  }
1395
1396
1397
0
  std::vector<uint8_t> data_array;
1398
0
  if (size > 0) { // do not memcpy() from a NULL pointer when there is no data
1399
0
    data_array.resize(size);
1400
0
    memcpy(data_array.data(), data, size);
1401
0
  }
1402
1403
0
  item->set_content_encoding(content_encoding);
1404
1405
  // copy the data into the file, store the pointer to it in an iloc box entry
1406
1407
0
  append_iloc_data(item->get_item_ID(), data_array, 0);
1408
1409
0
  return Error::Ok;
1410
0
}
1411
1412
1413
void HeifFile::append_iloc_data(heif_item_id id, const std::vector<uint8_t>& nal_packets, uint8_t construction_method)
1414
0
{
1415
0
  m_iloc_box->append_data(id, nal_packets, construction_method);
1416
0
}
1417
1418
1419
void HeifFile::replace_iloc_data(heif_item_id id, uint64_t offset, const std::vector<uint8_t>& data, uint8_t construction_method)
1420
0
{
1421
0
  m_iloc_box->replace_data(id, offset, data, construction_method);
1422
0
}
1423
1424
1425
void HeifFile::set_primary_item_id(heif_item_id id)
1426
480
{
1427
480
  if (!m_pitm_box) {
1428
480
    m_pitm_box = std::make_shared<Box_pitm>();
1429
480
    m_meta_box->replace_child_box(m_pitm_box);
1430
480
  }
1431
1432
480
  m_pitm_box->set_item_ID(id);
1433
480
}
1434
1435
1436
void HeifFile::set_ipco_box(const std::shared_ptr<Box_ipco>& ipco)
1437
371
{
1438
371
  m_ipco_box = ipco;
1439
371
  m_meta_box->replace_child_box(ipco);
1440
371
}
1441
1442
1443
void HeifFile::set_ipma_box(const std::shared_ptr<Box_ipma>& ipma)
1444
356
{
1445
356
  m_ipma_box = ipma;
1446
356
  m_meta_box->replace_child_box(ipma);
1447
356
}
1448
1449
1450
void HeifFile::set_iloc_box(const std::shared_ptr<Box_iloc>& iloc)
1451
356
{
1452
356
  m_iloc_box = iloc;
1453
356
  m_meta_box->replace_child_box(iloc);
1454
356
}
1455
1456
1457
void HeifFile::set_iref_box(const std::shared_ptr<Box_iref>& iref)
1458
356
{
1459
356
  m_iref_box = iref;
1460
356
  m_meta_box->replace_child_box(iref);
1461
356
}
1462
1463
1464
void HeifFile::add_iref_reference(heif_item_id from, uint32_t type,
1465
                                  const std::vector<heif_item_id>& to)
1466
0
{
1467
0
  if (!m_iref_box) {
1468
0
    m_iref_box = std::make_shared<Box_iref>();
1469
0
    m_meta_box->append_child_box(m_iref_box);
1470
0
  }
1471
1472
0
  m_iref_box->add_references(from, type, to);
1473
0
}
1474
1475
1476
void HeifFile::set_iref_reference(heif_item_id from, uint32_t type, int reference_idx, heif_item_id to_item)
1477
0
{
1478
0
  assert(m_iref_box);
1479
0
  m_iref_box->overwrite_reference(from, type, reference_idx, to_item);
1480
0
}
1481
1482
1483
void HeifFile::add_entity_group_box(const std::shared_ptr<Box>& entity_group_box)
1484
0
{
1485
0
  if (!m_grpl_box) {
1486
0
    m_grpl_box = std::make_shared<Box_grpl>();
1487
0
    m_meta_box->append_child_box(m_grpl_box);
1488
0
  }
1489
1490
0
  m_grpl_box->append_child_box(entity_group_box);
1491
0
}
1492
1493
1494
std::shared_ptr<Box_EntityToGroup> HeifFile::get_entity_group(heif_entity_group_id id)
1495
0
{
1496
0
  if (!m_grpl_box) {
1497
0
    return nullptr;
1498
0
  }
1499
1500
0
  const auto& entityGroups = m_grpl_box->get_all_child_boxes();
1501
0
  for (auto& groupBase : entityGroups) {
1502
0
    auto group = std::dynamic_pointer_cast<Box_EntityToGroup>(groupBase);
1503
0
    assert(group);
1504
1505
0
    if (group->get_group_id() == id) {
1506
0
      return group;
1507
0
    }
1508
0
  }
1509
1510
0
  return nullptr;
1511
0
}
1512
1513
1514
Error HeifFile::set_auxC_property(heif_item_id id, const std::string& type)
1515
0
{
1516
0
  init_for_item_properties();
1517
1518
0
  auto auxC = std::make_shared<Box_auxC>();
1519
0
  auxC->set_aux_type(type);
1520
1521
0
  uint32_t index = m_ipco_box->find_or_append_child_box(auxC);
1522
0
  if (index + 1 > 0xFFFF) {
1523
0
    return {heif_error_Encoding_error, heif_suberror_Unspecified, too_many_properties_error};
1524
0
  }
1525
1526
0
  m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
1527
1528
0
  return Error::Ok;
1529
0
}
1530
1531
#if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER)
1532
std::wstring HeifFile::convert_utf8_path_to_utf16(std::string str)
1533
{
1534
  std::wstring ret;
1535
  int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), NULL, 0);
1536
  if (len > 0)
1537
  {
1538
    ret.resize(len);
1539
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), &ret[0], len);
1540
  }
1541
  return ret;
1542
}
1543
#endif
1544
1545
1546
Result<size_t> HeifFile::write_mdat(StreamWriter& writer)
1547
0
{
1548
  // --- write mdat box
1549
1550
0
  size_t mdatSize = m_mdat_data->get_data_size();
1551
1552
0
  if (mdatSize <= 0xFFFFFFFF - 8) {
1553
0
    writer.write32((uint32_t) (mdatSize + 8));
1554
0
    writer.write32(fourcc("mdat"));
1555
0
  }
1556
0
  else {
1557
    // box size > 4 GB
1558
1559
0
    writer.write32(1);
1560
0
    writer.write32(fourcc("mdat"));
1561
0
    writer.write64(mdatSize+8+8);
1562
0
  }
1563
1564
0
  size_t dataStartPos = writer.get_position();
1565
1566
0
  m_mdat_data->write(writer);
1567
1568
0
  return dataStartPos;
1569
0
}