Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions examples/heif_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@
#endif

#include <libheif/heif.h>
#include <libheif/heif_library.h>
#include <libheif/heif_regions.h>
#include <libheif/heif_properties.h>
#include <libheif/heif_experimental.h>
#include "libheif/heif_sequences.h"
#include <libheif/heif_text.h>

#include <fstream>
#include <iostream>
Expand Down Expand Up @@ -639,6 +641,34 @@ int main(int argc, char** argv)
printf(" none\n");
}

// --- text items
int numTextItems = heif_image_handle_get_number_of_text_items(handle);
printf("text items:\n");

if (numTextItems > 0) {
std::vector<heif_item_id> text_items(numTextItems);
heif_image_handle_get_list_of_text_item_ids(handle, text_items.data(), numTextItems);
for (heif_item_id text_item_id : text_items) {
struct heif_text_item* text_item;
err = heif_context_get_text_item(ctx.get(), text_item_id, &text_item);
const char* text_content = heif_text_item_get_content(text_item);
printf(" text item: %s\n", text_content);
heif_string_release(text_content);

char* elng;
err = heif_item_get_property_extended_language(ctx.get(),
text_item_id,
&elng);
if (err.code == 0) {
printf(" extended language: %s\n", elng);
}
heif_string_release(elng);
}
}
else {
printf(" none\n");
}

// --- properties

printf("properties:\n");
Expand Down
3 changes: 2 additions & 1 deletion libheif/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,8 @@ WARN_LOGFILE =

INPUT = @CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif.h \
@CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_items.h \
@CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_regions.h
@CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_regions.h \
@CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_text.h

# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
Expand Down
3 changes: 2 additions & 1 deletion libheif/api/libheif/heif_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ enum heif_item_property_type
heif_item_property_type_image_size = heif_fourcc('i', 's', 'p', 'e'),
heif_item_property_type_uuid = heif_fourcc('u', 'u', 'i', 'd'),
heif_item_property_type_tai_clock_info = heif_fourcc('t', 'a', 'i', 'c'),
heif_item_property_type_tai_timestamp = heif_fourcc('i', 't', 'a', 'i')
heif_item_property_type_tai_timestamp = heif_fourcc('i', 't', 'a', 'i'),
heif_item_property_type_extended_language = heif_fourcc('e', 'l', 'n', 'g')
};

// Get the heif_property_id for a heif_item_id.
Expand Down
50 changes: 49 additions & 1 deletion libheif/api/libheif/heif_text.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@

#include "heif_text.h"
#include "api_structs.h"
#include "file.h"
#include "text.h"
#include <algorithm>
#include <cstring>
#include <memory>
#include <utility>
#include <string>
#include <utility>
#include <vector>

struct heif_error heif_image_handle_add_text_item(heif_image_handle *image_handle,
const char *content_type,
Expand Down Expand Up @@ -114,3 +116,49 @@ const char* heif_text_item_get_content(struct 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)
{
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;
}

struct 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;
}
}

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 (out_optional_propertyId) {
*out_optional_propertyId = id;
}

return heif_error_success;
}
31 changes: 30 additions & 1 deletion libheif/api/libheif/heif_text.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ heif_error heif_context_get_text_item(const heif_context* context,
/**
* Get the item identifier for a text item.
*
* @param region_item the text item to query
* @param text_item the text item to query
* @return the text item identifier (or 0 if the text_item is null)
*/
LIBHEIF_API
Expand All @@ -103,6 +103,19 @@ heif_item_id heif_text_item_get_id(heif_text_item* text_item);
LIBHEIF_API
const char* heif_text_item_get_content(heif_text_item* text_item);

/**
* Get the extended language associated with the text item.
*
* @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
*/
LIBHEIF_API
heif_error heif_item_get_property_extended_language(const heif_context* context,
heif_item_id itemId,
char** out_language);

// --- adding text items

/**
Expand All @@ -124,6 +137,22 @@ heif_error heif_image_handle_add_text_item(heif_image_handle *image_handle,
LIBHEIF_API
void heif_text_item_release(heif_text_item* text_item);

/**
* Set the extended language property to the text item.
*
* This adds an RFC 5346 (IETF BCP 47) extended language tag, such as "en-AU".
*
* @param text_item the text item to query
* @param language the language to set
* @param out_optional_propertyId Output parameter for the property ID of the language property.
* This parameter may be NULL if the info is not required.
* @return heif_error_ok on success, or an error value indicating the problem
*/
LIBHEIF_API
heif_error heif_text_item_set_extended_language(heif_text_item* text_item,
const char *language,
heif_property_id* out_optional_propertyId);

#ifdef __cplusplus
}
#endif
Expand Down
32 changes: 32 additions & 0 deletions libheif/box.cc
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,11 @@ Error Box::read(BitstreamRange& range, std::shared_ptr<Box>* result, const heif_
box = std::make_shared<Box_mjpg>();
break;

case fourcc("elng"):
box = std::make_shared<Box_elng>();
break;


#if WITH_UNCOMPRESSED_CODEC
case fourcc("cmpd"):
box = std::make_shared<Box_cmpd>();
Expand Down Expand Up @@ -4990,3 +4995,30 @@ Error Box_itai::parse(BitstreamRange& range, const heif_security_limits*) {
return range.get_error();
}

Error Box_elng::parse(BitstreamRange& range, const heif_security_limits* limits)
{
parse_full_box_header(range);

if (get_version() > 0) {
return unsupported_version_error("elng");
}

m_lang = range.read_string();
return range.get_error();
}

std::string Box_elng::dump(Indent& indent) const
{
std::ostringstream sstr;
sstr << Box::dump(indent);
sstr << indent << "extended_language: " << m_lang << "\n";
return sstr.str();
}

Error Box_elng::write(StreamWriter& writer) const
{
size_t box_start = reserve_box_header_space(writer);
writer.write(m_lang);
prepend_header(writer, box_start);
return Error::Ok;
}
49 changes: 48 additions & 1 deletion libheif/box.h
Original file line number Diff line number Diff line change
Expand Up @@ -1818,4 +1818,51 @@ class Box_itai : public FullBox
bool operator==(const heif_tai_timestamp_packet& a,
const heif_tai_timestamp_packet& b);

#endif

/**
* Extended language property.
*
* Permits the association of language information with an item.
*
* See ISO/IEC 23008-12:2025(E) Section 6.10.2.2 and ISO/IEC 14496-12:2022(E) Section 8.4.6.
*/
class Box_elng : public FullBox
{
public:
Box_elng()
{
set_short_type(fourcc("elng"));
}

std::string dump(Indent&) const override;

const char* debug_box_name() const override { return "Extended language"; }

Error write(StreamWriter& writer) const override;

/**
* Language.
*
* An RFC 5646 (IETF BCP 47) compliant language identifier for the language of the text.
* Examples: "en-AU", "de-DE", or "zh-CN“.
*/
std::string get_extended_language() const { return m_lang; }

/**
* Set the language.
*
* An RFC 5646 (IETF BCP 47) compliant language identifier for the language of the text.
* Examples: "en-AU", "de-DE", or "zh-CN“.
*/
void set_lang(const std::string lang) { m_lang = lang; }

[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }

protected:
Error parse(BitstreamRange& range, const heif_security_limits*) override;

private:
std::string m_lang;
};

#endif
19 changes: 19 additions & 0 deletions tests/text.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <cstdint>
#include <cstring>
#include <libheif/heif_items.h>
#include <libheif/heif_library.h>


TEST_CASE("no text") {
Expand Down Expand Up @@ -83,11 +84,17 @@ TEST_CASE("create text item") {
std::string text_body1("first string");
err = heif_image_handle_add_text_item(handle, "text/plain", text_body1.c_str(), &text_item1);
REQUIRE(err.code == heif_error_Ok);
err = heif_text_item_set_extended_language(text_item1, "en-AU", NULL);
REQUIRE(err.code == heif_error_Ok);

struct heif_text_item* text_item2;
std::string text_body2("a second string");
err = heif_image_handle_add_text_item(handle, "text/plain", text_body2.c_str(), &text_item2);
REQUIRE(err.code == heif_error_Ok);
heif_property_id elng_prop_id;
err = heif_text_item_set_extended_language(text_item2, "en-UK", &elng_prop_id);
REQUIRE(err.code == heif_error_Ok);
REQUIRE(elng_prop_id != 0);

err = heif_context_write_to_file(ctx, "text.heif");
REQUIRE(err.code == heif_error_Ok);
Expand Down Expand Up @@ -128,6 +135,12 @@ TEST_CASE("create text item") {
REQUIRE(std::string(content_type0) == "text/plain");
const char* body0 = heif_text_item_get_content(text0);
REQUIRE(std::string(body0) == text_body1);
heif_string_release(body0);
char * elng0;
err = heif_item_get_property_extended_language(readbackCtx, id0, &elng0);
REQUIRE(err.code == heif_error_Ok);
REQUIRE(strcmp(elng0, "en-AU") == 0);
heif_string_release(elng0);

heif_text_item* text1;
err = heif_context_get_text_item(readbackCtx, text_item_ids[1], &text1);
Expand All @@ -139,6 +152,12 @@ TEST_CASE("create text item") {
REQUIRE(std::string(content_type1) == "text/plain");
const char* body1 = heif_text_item_get_content(text1);
REQUIRE(std::string(body1) == text_body2);
heif_string_release(body1);
char * elng1;
err = heif_item_get_property_extended_language(readbackCtx, id1, &elng1);
REQUIRE(err.code == heif_error_Ok);
REQUIRE(strcmp(elng1, "en-UK") == 0);
heif_string_release(elng1);

heif_text_item_release(text0);
heif_text_item_release(text1);
Expand Down
Loading