Commit 77ed03de for libheif

commit 77ed03dea8562ac6ace1ef487d9149741212d47d
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Sun Dec 21 23:51:48 2025 +0100

    item language 'elng' can be get/set using either plain items or text-items (#1559)

diff --git a/libheif/api/libheif/heif_items.cc b/libheif/api/libheif/heif_items.cc
index 09e9a1e2..2abdcf87 100644
--- a/libheif/api/libheif/heif_items.cc
+++ b/libheif/api/libheif/heif_items.cc
@@ -182,6 +182,50 @@ void heif_release_item_data(const heif_context* ctx, uint8_t** item_data)
 }


+heif_error heif_item_get_property_extended_language(const heif_context* context,
+                                                    heif_item_id itemId,
+                                                    char** out_language)
+{
+  if (!out_language || !context) {
+    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"};
+  }
+
+  auto elng = context->context->find_property<Box_elng>(itemId);
+  if (!elng) {
+    return elng.error_struct(context->context.get());
+  }
+
+  std::string lang = (*elng)->get_extended_language();
+  *out_language = new char[lang.length() + 1];
+  strcpy(*out_language, lang.c_str());
+
+  return heif_error_success;
+}
+
+
+heif_error heif_item_set_extended_language(heif_context* context,
+                                           heif_item_id item_id,
+                                           const char* language, heif_property_id* out_optional_propertyId)
+{
+  if (!context || !language) {
+    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"};
+  }
+
+  Result<heif_property_id> property_id_result = context->context->add_text_property(item_id,
+                                                                                    language);
+
+  if (auto err = property_id_result.error()) {
+    return err.error_struct(context->context.get());
+  }
+
+  if (out_optional_propertyId) {
+    *out_optional_propertyId = *property_id_result;
+  }
+
+  return heif_error_success;
+}
+
+
 size_t heif_context_get_item_references(const heif_context* ctx,
                                         heif_item_id from_item_id,
                                         int index,
diff --git a/libheif/api/libheif/heif_items.h b/libheif/api/libheif/heif_items.h
index 5448375b..1e179f0e 100644
--- a/libheif/api/libheif/heif_items.h
+++ b/libheif/api/libheif/heif_items.h
@@ -167,6 +167,27 @@ LIBHEIF_API
 void heif_release_item_data(const heif_context* ctx, uint8_t** item_data);


+// ------------------------- item language -------------------------
+
+/**
+ * Get the extended language associated with the item.
+ * The item is usually a text item.
+ *
+ * @param context the heif file context containg the item.
+ * @param itemId the identifier for the item
+ * @param out_language output parameter with the item's language. Free with heif_string_release().
+ * @return heif_error_ok on success, or an error value indicating the problem
+ */
+LIBHEIF_API
+heif_error heif_item_get_property_extended_language(const heif_context* context,
+                                                    heif_item_id itemId,
+                                                    char** out_language);
+
+LIBHEIF_API
+heif_error heif_item_set_extended_language(heif_context* context,
+                                           heif_item_id item_id,
+                                           const char* language, heif_property_id* out_optional_propertyId);
+
 // ------------------------- item references -------------------------

 /**
diff --git a/libheif/api/libheif/heif_text.cc b/libheif/api/libheif/heif_text.cc
index 0b44d692..f4f3e204 100644
--- a/libheif/api/libheif/heif_text.cc
+++ b/libheif/api/libheif/heif_text.cc
@@ -117,17 +117,16 @@ const char* heif_text_item_get_content(heif_text_item* text_item)
   return text_c;
 }

-struct heif_error heif_item_get_property_extended_language(const heif_context* context,
-                                                           heif_item_id itemId,
-                                                           char** out_language)
+
+heif_error heif_text_item_get_property_extended_language(const heif_text_item* text_item, char** out_language)
 {
-  if (!out_language || !context) {
+  if (!out_language || !text_item) {
     return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"};
   }

-  auto elng = context->context->find_property<Box_elng>(itemId);
+  auto elng = text_item->context->find_property<Box_elng>(text_item->text_item->get_item_id());
   if (!elng) {
-    return elng.error_struct(context->context.get());
+    return elng.error_struct(text_item->context.get());
   }

   std::string lang = (*elng)->get_extended_language();
@@ -137,27 +136,22 @@ struct heif_error heif_item_get_property_extended_language(const heif_context* c
   return heif_error_success;
 }

-struct heif_error heif_text_item_set_extended_language(heif_text_item* text_item, const char *language, heif_property_id* out_optional_propertyId)
+
+heif_error heif_text_item_set_extended_language(heif_text_item* text_item, const char *language, heif_property_id* out_optional_propertyId)
 {
   if (!text_item || !language) {
     return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"};
   }

-  if (auto img = text_item->context->get_image(text_item->text_item->get_item_id(), false)) {
-    auto existing_elng = img->get_property<Box_elng>();
-    if (existing_elng) {
-      existing_elng->set_lang(std::string(language));
-      return heif_error_success;
-    }
-  }
+  Result<heif_property_id> property_id_result = text_item->context->add_text_property(text_item->text_item->get_item_id(),
+                                                                                      language);

-  auto elng = std::make_shared<Box_elng>();
-  elng->set_lang(std::string(language));
-
-  heif_property_id id = text_item->context->add_property(text_item->text_item->get_item_id(), elng, false);
+  if (auto err = property_id_result.error()) {
+    return err.error_struct(text_item->context.get());
+  }

   if (out_optional_propertyId) {
-    *out_optional_propertyId = id;
+    *out_optional_propertyId = *property_id_result;
   }

   return heif_error_success;
diff --git a/libheif/api/libheif/heif_text.h b/libheif/api/libheif/heif_text.h
index e46e1487..943f4d1a 100644
--- a/libheif/api/libheif/heif_text.h
+++ b/libheif/api/libheif/heif_text.h
@@ -104,17 +104,16 @@ LIBHEIF_API
 const char* heif_text_item_get_content(heif_text_item* text_item);

 /**
- * Get the extended language associated with the text item.
+ * This function is similar to heif_item_get_property_extended_language(), but
+ * takes a `heif_text_item` as parameter.
  *
- * @param context the context to get the text item from, usually from a file operation
- * @param itemId the identifier for the text item
- * @param out_language pointer to pointer to the resulting language
- * @return heif_error_ok on success, or an error value indicating the problem
+ * @param text_item The text item for which we are requesting the language.
+ * @param out_language Output parameter for the text language. Free with heif_string_release().
+ * @return
  */
 LIBHEIF_API
-heif_error heif_item_get_property_extended_language(const heif_context* context,
-                                                    heif_item_id itemId,
-                                                    char** out_language);
+heif_error heif_text_item_get_property_extended_language(const heif_text_item* text_item,
+                                                         char** out_language);

 // --- adding text items

diff --git a/libheif/context.cc b/libheif/context.cc
index e5f3d2fa..a9b3143b 100644
--- a/libheif/context.cc
+++ b/libheif/context.cc
@@ -1844,6 +1844,24 @@ Result<heif_item_id> HeifContext::add_pyramid_group(const std::vector<heif_item_
 }


+Result<heif_property_id> HeifContext::add_text_property(heif_item_id itemId, const std::string& language)
+{
+  if (find_property<Box_elng>(itemId)) {
+    return Error{
+      heif_error_Usage_error,
+      heif_suberror_Unspecified,
+      "Item already has an 'elng' language property."
+    };
+  }
+
+  auto elng = std::make_shared<Box_elng>();
+  elng->set_lang(std::string(language));
+
+  heif_property_id id = add_property(itemId, elng, false);
+  return id;
+}
+
+
 Error HeifContext::interpret_heif_file_sequences()
 {
   m_tracks.clear();
diff --git a/libheif/context.h b/libheif/context.h
index ae3c8148..af1359cc 100644
--- a/libheif/context.h
+++ b/libheif/context.h
@@ -165,6 +165,9 @@ public:

   Result<heif_item_id> add_pyramid_group(const std::vector<heif_item_id>& layers);

+  Result<heif_property_id> add_text_property(heif_item_id, const std::string& language);
+
+
   // --- region items

   void add_region_item(std::shared_ptr<RegionItem> region_item)
@@ -271,6 +274,14 @@ public:
     return result;
   }

+  template<typename T>
+  bool has_property(heif_item_id itemId) const
+  {
+    auto file = this->get_heif_file();
+    auto result = file->get_property_for_item<T>(itemId);
+    return result != nullptr;
+  }
+
 private:
   std::map<heif_item_id, std::shared_ptr<ImageItem>> m_all_images;