Skip to content

Commit a1de57a

Browse files
committed
text: add extended language property support
1 parent 72b7fcd commit a1de57a

File tree

8 files changed

+212
-5
lines changed

8 files changed

+212
-5
lines changed

examples/heif_info.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@
3838
#endif
3939

4040
#include <libheif/heif.h>
41+
#include <libheif/heif_library.h>
4142
#include <libheif/heif_regions.h>
4243
#include <libheif/heif_properties.h>
4344
#include <libheif/heif_experimental.h>
4445
#include "libheif/heif_sequences.h"
46+
#include <libheif/heif_text.h>
4547

4648
#include <fstream>
4749
#include <iostream>
@@ -639,6 +641,34 @@ int main(int argc, char** argv)
639641
printf(" none\n");
640642
}
641643

644+
// --- text items
645+
int numTextItems = heif_image_handle_get_number_of_text_items(handle);
646+
printf("text items:\n");
647+
648+
if (numTextItems > 0) {
649+
std::vector<heif_item_id> text_items(numTextItems);
650+
heif_image_handle_get_list_of_text_item_ids(handle, text_items.data(), numTextItems);
651+
for (heif_item_id text_item_id : text_items) {
652+
struct heif_text_item* text_item;
653+
err = heif_context_get_text_item(ctx.get(), text_item_id, &text_item);
654+
const char* text_content = heif_text_item_get_content(text_item);
655+
printf(" text item: %s\n", text_content);
656+
heif_string_release(text_content);
657+
658+
char* elng;
659+
err = heif_item_get_property_extended_language(ctx.get(),
660+
text_item_id,
661+
&elng);
662+
if (err.code == 0) {
663+
printf(" extended language: %s\n", elng);
664+
}
665+
heif_string_release(elng);
666+
}
667+
}
668+
else {
669+
printf(" none\n");
670+
}
671+
642672
// --- properties
643673

644674
printf("properties:\n");

libheif/Doxyfile.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,8 @@ WARN_LOGFILE =
858858

859859
INPUT = @CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif.h \
860860
@CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_items.h \
861-
@CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_regions.h
861+
@CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_regions.h \
862+
@CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_text.h
862863

863864
# This tag can be used to specify the character encoding of the source files
864865
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

libheif/api/libheif/heif_properties.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ enum heif_item_property_type
4040
heif_item_property_type_image_size = heif_fourcc('i', 's', 'p', 'e'),
4141
heif_item_property_type_uuid = heif_fourcc('u', 'u', 'i', 'd'),
4242
heif_item_property_type_tai_clock_info = heif_fourcc('t', 'a', 'i', 'c'),
43-
heif_item_property_type_tai_timestamp = heif_fourcc('i', 't', 'a', 'i')
43+
heif_item_property_type_tai_timestamp = heif_fourcc('i', 't', 'a', 'i'),
44+
heif_item_property_type_extended_language = heif_fourcc('e', 'l', 'n', 'g')
4445
};
4546

4647
// Get the heif_property_id for a heif_item_id.

libheif/api/libheif/heif_text.cc

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121

2222
#include "heif_text.h"
2323
#include "api_structs.h"
24+
#include "file.h"
2425
#include "text.h"
2526
#include <algorithm>
2627
#include <cstring>
2728
#include <memory>
28-
#include <utility>
2929
#include <string>
30+
#include <utility>
31+
#include <vector>
3032

3133
struct heif_error heif_image_handle_add_text_item(heif_image_handle *image_handle,
3234
const char *content_type,
@@ -114,3 +116,49 @@ const char* heif_text_item_get_content(struct heif_text_item* text_item)
114116

115117
return text_c;
116118
}
119+
120+
struct heif_error heif_item_get_property_extended_language(const heif_context* context,
121+
heif_item_id itemId,
122+
char** out_language)
123+
{
124+
if (!out_language || !context) {
125+
return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"};
126+
}
127+
128+
auto elng = context->context->find_property<Box_elng>(itemId);
129+
if (!elng) {
130+
return elng.error_struct(context->context.get());
131+
}
132+
133+
std::string lang = (*elng)->get_extended_language();
134+
*out_language = new char[lang.length() + 1];
135+
strcpy(*out_language, lang.c_str());
136+
137+
return heif_error_success;
138+
}
139+
140+
struct heif_error heif_text_item_set_extended_language(heif_text_item* text_item, const char *language, heif_property_id* out_optional_propertyId)
141+
{
142+
if (!text_item || !language) {
143+
return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"};
144+
}
145+
146+
if (auto img = text_item->context->get_image(text_item->text_item->get_item_id(), false)) {
147+
auto existing_elng = img->get_property<Box_elng>();
148+
if (existing_elng) {
149+
existing_elng->set_lang(std::string(language));
150+
return heif_error_success;
151+
}
152+
}
153+
154+
auto elng = std::make_shared<Box_elng>();
155+
elng->set_lang(std::string(language));
156+
157+
heif_property_id id = text_item->context->add_property(text_item->text_item->get_item_id(), elng, false);
158+
159+
if (out_optional_propertyId) {
160+
*out_optional_propertyId = id;
161+
}
162+
163+
return heif_error_success;
164+
}

libheif/api/libheif/heif_text.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ heif_error heif_context_get_text_item(const heif_context* context,
8585
/**
8686
* Get the item identifier for a text item.
8787
*
88-
* @param region_item the text item to query
88+
* @param text_item the text item to query
8989
* @return the text item identifier (or 0 if the text_item is null)
9090
*/
9191
LIBHEIF_API
@@ -103,6 +103,19 @@ heif_item_id heif_text_item_get_id(heif_text_item* text_item);
103103
LIBHEIF_API
104104
const char* heif_text_item_get_content(heif_text_item* text_item);
105105

106+
/**
107+
* Get the extended language associated with the text item.
108+
*
109+
* @param context the context to get the text item from, usually from a file operation
110+
* @param itemId the identifier for the text item
111+
* @param out_language pointer to pointer to the resulting language
112+
* @return heif_error_ok on success, or an error value indicating the problem
113+
*/
114+
LIBHEIF_API
115+
heif_error heif_item_get_property_extended_language(const heif_context* context,
116+
heif_item_id itemId,
117+
char** out_language);
118+
106119
// --- adding text items
107120

108121
/**
@@ -124,6 +137,22 @@ heif_error heif_image_handle_add_text_item(heif_image_handle *image_handle,
124137
LIBHEIF_API
125138
void heif_text_item_release(heif_text_item* text_item);
126139

140+
/**
141+
* Set the extended language property to the text item.
142+
*
143+
* This adds an RFC 5346 (IETF BCP 47) extended language tag, such as "en-AU".
144+
*
145+
* @param text_item the text item to query
146+
* @param language the language to set
147+
* @param out_optional_propertyId Output parameter for the property ID of the language property.
148+
* This parameter may be NULL if the info is not required.
149+
* @return heif_error_ok on success, or an error value indicating the problem
150+
*/
151+
LIBHEIF_API
152+
heif_error heif_text_item_set_extended_language(heif_text_item* text_item,
153+
const char *language,
154+
heif_property_id* out_optional_propertyId);
155+
127156
#ifdef __cplusplus
128157
}
129158
#endif

libheif/box.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,11 @@ Error Box::read(BitstreamRange& range, std::shared_ptr<Box>* result, const heif_
646646
box = std::make_shared<Box_mjpg>();
647647
break;
648648

649+
case fourcc("elng"):
650+
box = std::make_shared<Box_elng>();
651+
break;
652+
653+
649654
#if WITH_UNCOMPRESSED_CODEC
650655
case fourcc("cmpd"):
651656
box = std::make_shared<Box_cmpd>();
@@ -4990,3 +4995,30 @@ Error Box_itai::parse(BitstreamRange& range, const heif_security_limits*) {
49904995
return range.get_error();
49914996
}
49924997

4998+
Error Box_elng::parse(BitstreamRange& range, const heif_security_limits* limits)
4999+
{
5000+
parse_full_box_header(range);
5001+
5002+
if (get_version() > 0) {
5003+
return unsupported_version_error("elng");
5004+
}
5005+
5006+
m_lang = range.read_string();
5007+
return range.get_error();
5008+
}
5009+
5010+
std::string Box_elng::dump(Indent& indent) const
5011+
{
5012+
std::ostringstream sstr;
5013+
sstr << Box::dump(indent);
5014+
sstr << indent << "extended_language: " << m_lang << "\n";
5015+
return sstr.str();
5016+
}
5017+
5018+
Error Box_elng::write(StreamWriter& writer) const
5019+
{
5020+
size_t box_start = reserve_box_header_space(writer);
5021+
writer.write(m_lang);
5022+
prepend_header(writer, box_start);
5023+
return Error::Ok;
5024+
}

libheif/box.h

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1818,4 +1818,51 @@ class Box_itai : public FullBox
18181818
bool operator==(const heif_tai_timestamp_packet& a,
18191819
const heif_tai_timestamp_packet& b);
18201820

1821-
#endif
1821+
1822+
/**
1823+
* Extended language property.
1824+
*
1825+
* Permits the association of language information with an item.
1826+
*
1827+
* See ISO/IEC 23008-12:2025(E) Section 6.10.2.2 and ISO/IEC 14496-12:2022(E) Section 8.4.6.
1828+
*/
1829+
class Box_elng : public FullBox
1830+
{
1831+
public:
1832+
Box_elng()
1833+
{
1834+
set_short_type(fourcc("elng"));
1835+
}
1836+
1837+
std::string dump(Indent&) const override;
1838+
1839+
const char* debug_box_name() const override { return "Extended language"; }
1840+
1841+
Error write(StreamWriter& writer) const override;
1842+
1843+
/**
1844+
* Language.
1845+
*
1846+
* An RFC 5646 (IETF BCP 47) compliant language identifier for the language of the text.
1847+
* Examples: "en-AU", "de-DE", or "zh-CN“.
1848+
*/
1849+
std::string get_extended_language() const { return m_lang; }
1850+
1851+
/**
1852+
* Set the language.
1853+
*
1854+
* An RFC 5646 (IETF BCP 47) compliant language identifier for the language of the text.
1855+
* Examples: "en-AU", "de-DE", or "zh-CN“.
1856+
*/
1857+
void set_lang(const std::string lang) { m_lang = lang; }
1858+
1859+
[[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
1860+
1861+
protected:
1862+
Error parse(BitstreamRange& range, const heif_security_limits*) override;
1863+
1864+
private:
1865+
std::string m_lang;
1866+
};
1867+
1868+
#endif

tests/text.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <cstdint>
3434
#include <cstring>
3535
#include <libheif/heif_items.h>
36+
#include <libheif/heif_library.h>
3637

3738

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

8790
struct heif_text_item* text_item2;
8891
std::string text_body2("a second string");
8992
err = heif_image_handle_add_text_item(handle, "text/plain", text_body2.c_str(), &text_item2);
9093
REQUIRE(err.code == heif_error_Ok);
94+
heif_property_id elng_prop_id;
95+
err = heif_text_item_set_extended_language(text_item2, "en-UK", &elng_prop_id);
96+
REQUIRE(err.code == heif_error_Ok);
97+
REQUIRE(elng_prop_id != 0);
9198

9299
err = heif_context_write_to_file(ctx, "text.heif");
93100
REQUIRE(err.code == heif_error_Ok);
@@ -128,6 +135,12 @@ TEST_CASE("create text item") {
128135
REQUIRE(std::string(content_type0) == "text/plain");
129136
const char* body0 = heif_text_item_get_content(text0);
130137
REQUIRE(std::string(body0) == text_body1);
138+
heif_string_release(body0);
139+
char * elng0;
140+
err = heif_item_get_property_extended_language(readbackCtx, id0, &elng0);
141+
REQUIRE(err.code == heif_error_Ok);
142+
REQUIRE(strcmp(elng0, "en-AU") == 0);
143+
heif_string_release(elng0);
131144

132145
heif_text_item* text1;
133146
err = heif_context_get_text_item(readbackCtx, text_item_ids[1], &text1);
@@ -139,6 +152,12 @@ TEST_CASE("create text item") {
139152
REQUIRE(std::string(content_type1) == "text/plain");
140153
const char* body1 = heif_text_item_get_content(text1);
141154
REQUIRE(std::string(body1) == text_body2);
155+
heif_string_release(body1);
156+
char * elng1;
157+
err = heif_item_get_property_extended_language(readbackCtx, id1, &elng1);
158+
REQUIRE(err.code == heif_error_Ok);
159+
REQUIRE(strcmp(elng1, "en-UK") == 0);
160+
heif_string_release(elng1);
142161

143162
heif_text_item_release(text0);
144163
heif_text_item_release(text1);

0 commit comments

Comments
 (0)