Commit 248c1de0 for libheif

commit 248c1de03b3264c24e6c515b8c86c2161744ae65
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Fri Feb 27 16:35:21 2026 +0100

    allow adding MIME item to sequence only file

diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc
index 2642bf61..8aa58ce3 100644
--- a/examples/heif_enc.cc
+++ b/examples/heif_enc.cc
@@ -1283,6 +1283,7 @@ public:

 int do_encode_images(heif_context*, heif_encoder*, heif_encoding_options* options, const std::vector<std::string>& args);
 int do_encode_sequence(heif_context*, heif_encoder*, heif_encoding_options* options, std::vector<std::string> args);
+int add_mime_item(heif_context* context);


 int main(int argc, char** argv)
@@ -1672,10 +1673,6 @@ int main(int argc, char** argv)
     return 5;
   }

-  if (encode_sequence && !option_mime_item_file.empty()) {
-    std::cerr << "MIME item cannot be added to sequence-only files.\n";
-    return 5;
-  }

   if (!option_sai_data_file.empty() && !encode_sequence) {
     std::cerr << "Image SAI data can only be used with sequences.\n";
@@ -1889,6 +1886,15 @@ int main(int argc, char** argv)
     return ret;
   }

+  // --- add extra MIME item with user data
+
+  ret = add_mime_item(context.get());
+  if (ret != 0) {
+    heif_encoding_options_free(options);
+    heif_encoder_release(encoder);
+    return ret;
+  }
+

   // --- write HEIF file

@@ -1909,6 +1915,48 @@ int main(int argc, char** argv)
 }


+int add_mime_item(heif_context* context)
+{
+  if (!option_mime_item_file.empty() || !option_mime_item_type.empty()) {
+    if (option_mime_item_file.empty() || option_mime_item_type.empty()) {
+      std::cerr << "Options --add-mime-item and --mime-item-file have to be used together\n";
+      return 5;
+    }
+
+    std::ifstream istr(option_mime_item_file.c_str(), std::ios::binary | std::ios::ate);
+    if (!istr) {
+      std::cerr << "Failed to open file for MIME item: '" << option_mime_item_file << "'\n";
+      return 5;
+    }
+
+    // Get size by seeking to the end (thanks to ios::ate)
+    std::streamsize size = istr.tellg();
+    if (size < 0) {
+      std::cerr << "Querying size of file '" << option_mime_item_file << "' failed.\n";
+      return 5;
+    }
+
+    std::vector<uint8_t> buffer(size);
+
+    // Seek back to beginning and read
+    istr.seekg(0, std::ios::beg);
+    istr.read(reinterpret_cast<char*>(buffer.data()), size);
+
+    heif_item_id itemId;
+    heif_context_add_mime_item(context, option_mime_item_type.c_str(),
+                               metadata_compression_method,
+                               buffer.data(), (int)buffer.size(),
+                               &itemId);
+
+    if (!option_mime_item_name.empty()) {
+      heif_item_set_item_name(context, itemId, option_mime_item_name.c_str());
+    }
+  }
+
+  return 0;
+}
+
+
 int do_encode_images(heif_context* context, heif_encoder* encoder, heif_encoding_options* options, const std::vector<std::string>& args)
 {
   std::shared_ptr<heif_image> primary_image;
@@ -2221,44 +2269,6 @@ int do_encode_images(heif_context* context, heif_encoder* encoder, heif_encoding
   }
 #endif

-  // --- add extra MIME item with user data
-
-  if (!option_mime_item_file.empty() || !option_mime_item_type.empty()) {
-    if (option_mime_item_file.empty() || option_mime_item_type.empty()) {
-      std::cerr << "Options --add-mime-item and --mime-item-file have to be used together\n";
-      return 5;
-    }
-
-    std::ifstream istr(option_mime_item_file.c_str(), std::ios::binary | std::ios::ate);
-    if (!istr) {
-      std::cerr << "Failed to open file for MIME item: '" << option_mime_item_file << "'\n";
-      return 5;
-    }
-
-    // Get size by seeking to the end (thanks to ios::ate)
-    std::streamsize size = istr.tellg();
-    if (size < 0) {
-      std::cerr << "Querying size of file '" << option_mime_item_file << "' failed.\n";
-      return 5;
-    }
-
-    std::vector<uint8_t> buffer(size);
-
-    // Seek back to beginning and read
-    istr.seekg(0, std::ios::beg);
-    istr.read(reinterpret_cast<char*>(buffer.data()), size);
-
-    heif_item_id itemId;
-    heif_context_add_mime_item(context, option_mime_item_type.c_str(),
-                               metadata_compression_method,
-                               buffer.data(), (int)buffer.size(),
-                               &itemId);
-
-    if (!option_mime_item_name.empty()) {
-      heif_item_set_item_name(context, itemId, option_mime_item_name.c_str());
-    }
-  }
-
   if (run_benchmark) {
     double psnr = compute_psnr(primary_image.get(), output_filename);
     std::cout << "PSNR: " << std::setprecision(2) << std::fixed << psnr << " ";
diff --git a/libheif/api/libheif/heif_image_handle.cc b/libheif/api/libheif/heif_image_handle.cc
index eb4facc9..20bf9468 100644
--- a/libheif/api/libheif/heif_image_handle.cc
+++ b/libheif/api/libheif/heif_image_handle.cc
@@ -28,6 +28,7 @@
 #include <cstring>
 #include <string>
 #include <memory>
+#include <vector>


 void heif_image_handle_release(const heif_image_handle* handle)
diff --git a/libheif/file.cc b/libheif/file.cc
index a652ae6a..3800e83b 100644
--- a/libheif/file.cc
+++ b/libheif/file.cc
@@ -163,39 +163,59 @@ void HeifFile::new_empty_file()
 }


-void HeifFile::init_for_image()
+void HeifFile::init_for_meta_item()
 {
-  if (m_meta_box) {
-    return;
+  if (!m_meta_box) {
+    m_meta_box = std::make_shared<Box_meta>();
+    m_top_level_boxes.push_back(m_meta_box);
   }

-  m_hdlr_box = std::make_shared<Box_hdlr>();
-  m_meta_box = std::make_shared<Box_meta>();
-  m_ipco_box = std::make_shared<Box_ipco>();
-  m_ipma_box = std::make_shared<Box_ipma>();
-  m_iloc_box = std::make_shared<Box_iloc>();
-  m_iinf_box = std::make_shared<Box_iinf>();
-  m_iprp_box = std::make_shared<Box_iprp>();
-  m_pitm_box = std::make_shared<Box_pitm>();
+  if (!m_hdlr_box) {
+    m_hdlr_box = std::make_shared<Box_hdlr>();
+    m_hdlr_box->set_handler_type(fourcc("null"));
+    m_meta_box->append_child_box(m_hdlr_box);
+  }

-  m_meta_box->append_child_box(m_hdlr_box);
-  m_meta_box->append_child_box(m_pitm_box);
-  m_meta_box->append_child_box(m_iloc_box);
-  m_meta_box->append_child_box(m_iinf_box);
-  m_meta_box->append_child_box(m_iprp_box);
+  if (!m_iloc_box) {
+    m_iloc_box = std::make_shared<Box_iloc>();
+    m_meta_box->append_child_box(m_iloc_box);
+  }

-  m_iprp_box->append_child_box(m_ipco_box);
-  m_iprp_box->append_child_box(m_ipma_box);
+  if (!m_iinf_box) {
+    m_iinf_box = std::make_shared<Box_iinf>();
+    m_meta_box->append_child_box(m_iinf_box);
+  }
+}

-  m_infe_boxes.clear();

-  m_top_level_boxes.push_back(m_meta_box);
-#if ENABLE_EXPERIMENTAL_MINI_FORMAT
-  // TODO: do not create 'mini' box as we cannot write them yet.
-  //       if we include it in the top_level_boxes, it will be written into every file.
-  //m_mini_box = std::make_shared<Box_mini>();
-  //m_top_level_boxes.push_back(m_mini_box);
-#endif
+void HeifFile::init_for_image()
+{
+  init_for_meta_item();
+
+  // Upgrade handler from "null" to "pict" if needed
+  if (m_hdlr_box->get_handler_type() == fourcc("null")) {
+    m_hdlr_box->set_handler_type(fourcc("pict"));
+  }
+
+  if (!m_pitm_box) {
+    m_pitm_box = std::make_shared<Box_pitm>();
+    m_meta_box->append_child_box(m_pitm_box);
+  }
+
+  if (!m_iprp_box) {
+    m_iprp_box = std::make_shared<Box_iprp>();
+    m_meta_box->append_child_box(m_iprp_box);
+  }
+
+  if (!m_ipco_box) {
+    m_ipco_box = std::make_shared<Box_ipco>();
+    m_iprp_box->append_child_box(m_ipco_box);
+  }
+
+  if (!m_ipma_box) {
+    m_ipma_box = std::make_shared<Box_ipma>();
+    m_iprp_box->append_child_box(m_ipma_box);
+  }
 }


@@ -890,6 +910,28 @@ Result<std::shared_ptr<Box_infe>> HeifFile::add_new_infe_box(uint32_t item_type)
 }


+Result<std::shared_ptr<Box_infe>> HeifFile::add_new_meta_infe_box(uint32_t item_type)
+{
+  init_for_meta_item();
+
+  auto idResult = get_unused_item_id();
+  if (!idResult) {
+    return idResult.error();
+  }
+  heif_item_id id = *idResult;
+
+  auto infe = std::make_shared<Box_infe>();
+  infe->set_item_ID(id);
+  infe->set_hidden_item(false);
+  infe->set_item_type_4cc(item_type);
+
+  m_infe_boxes[id] = infe;
+  m_iinf_box->append_child_box(infe);
+
+  return infe;
+}
+
+
 void HeifFile::add_ispe_property(heif_item_id id, uint32_t width, uint32_t height, bool essential)
 {
   auto ispe = std::make_shared<Box_ispe>();
@@ -1013,7 +1055,7 @@ Result<heif_item_id> HeifFile::add_infe_mime(const char* content_type, heif_meta
 {
   // create an infe box describing what kind of data we are storing (this also creates a new ID)

-  auto infe_result = add_new_infe_box(fourcc("mime"));
+  auto infe_result = add_new_meta_infe_box(fourcc("mime"));
   if (!infe_result) {
     return infe_result.error();
   }
@@ -1033,7 +1075,7 @@ Result<heif_item_id> HeifFile::add_precompressed_infe_mime(const char* content_t
 {
   // create an infe box describing what kind of data we are storing (this also creates a new ID)

-  auto infe_result = add_new_infe_box(fourcc("mime"));
+  auto infe_result = add_new_meta_infe_box(fourcc("mime"));
   if (!infe_result) {
     return infe_result.error();
   }
@@ -1053,7 +1095,7 @@ Result<heif_item_id> HeifFile::add_infe_uri(const char* item_uri_type, const uin
 {
   // create an infe box describing what kind of data we are storing (this also creates a new ID)

-  auto infe_result = add_new_infe_box(fourcc("uri "));
+  auto infe_result = add_new_meta_infe_box(fourcc("uri "));
   if (!infe_result) {
     return infe_result.error();
   }
diff --git a/libheif/file.h b/libheif/file.h
index c9360ec9..a76bc962 100644
--- a/libheif/file.h
+++ b/libheif/file.h
@@ -82,6 +82,8 @@ public:

   void new_empty_file();

+  void init_for_meta_item();
+
   void init_for_image();

   void init_for_sequence();
@@ -96,7 +98,7 @@ public:

   int get_num_images() const { return static_cast<int>(m_infe_boxes.size()); }

-  heif_item_id get_primary_image_ID() const { return m_pitm_box->get_item_ID(); }
+  heif_item_id get_primary_image_ID() const { return m_pitm_box ? m_pitm_box->get_item_ID() : 0; }

   size_t get_number_of_items() const { return m_infe_boxes.size(); }

@@ -194,6 +196,8 @@ public:

   Result<std::shared_ptr<Box_infe>> add_new_infe_box(uint32_t item_type);

+  Result<std::shared_ptr<Box_infe>> add_new_meta_infe_box(uint32_t item_type);
+
   void add_ispe_property(heif_item_id id, uint32_t width, uint32_t height, bool essential);

   // set irot/imir according to heif_orientation