How to get image data as text (export text an image)

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By I reject all deals

I want to get image file as as text. For example when we open an image with text editor it gives we a text. How to get the text with godot? And how to save it text to image again?

Generally, you can load and manipulate image data via the Image class. However, it’s not clear what you goals are. If you just want to load (and/or save) the raw binary data contained in an image file, you can do that via the File class.

What is is that you’re trying to do exactly?

jgodfrey | 2023-05-24 22:13

I mean to say for example when you open an any image file png,jpg in any text editor, it gives you a text like that:

“‰PNGIHDR @ @ ªiqŞ sRGB ®Îé £IDATxœå›ytTUžÇ?ïÕ«%•µ’@ȞY1JZ ±iAÆi°[P§Çeú¨c;İ.`Ow+4>½(}zºEFÏDm:ƒ h›°I HHBÈBR!{%•Zß›?ŠŠ U©T‘
Ì÷œ:çÕ]~÷÷ûİûîı-÷ ÃŒ¢{q*äh$e-
×)²’'Âd®b(ŠÒ.ˆB•6‡ğºJàÄ©=;†ê÷Cvñj„çE~Z„+ÂíCQ”AA­ üòä§;.Ô À^P ‚€¤…”

I want to get this text in godot, it means I want to open an image file in godot as a text. And if I can get this text, I want to save it to image again. Is it possible with godot?

I reject all deals | 2023-05-25 18:47

:bust_in_silhouette: Reply From: jgodfrey

Based on the above discussions…

I want to get this text in godot, it means I want to open an image file in godot as a text. And if I can get this text, I want to save it to image again. Is it possible with godot?

Yes, that’s certainly possible. So, really, you don’t want to treat the image file as an image at all, but rather just as a blob of binary data (for reasons that still aren’t clear).

As I suggested above, you can open, load, and save file data via the File class in Godot 3.x. Some examples:

To load the data:

var file = File.new()
file.open("c:/path/to/your/image.jpg", File.READ)
var content = file.get_as_text()
file.close()

To display the loaded text in a (for example) TextEdit control:

$TextEdit.text = content

To save the contents:

var file = File.new()
file.open("c:/path/to/your/image.jpg", File.WRITE)
file.store_string(content)
file.close()

Note that handling binary data as a “string” (via get_as_text() and store_string() may lead to unexpected results. To instead handle the raw, uninterpreted binary data you might consider get_buffer() and store_buffer().

I tried very similar way to do it but when I used “get_as_text” it doesnt gives any text. It shows its empty. Why? But I will try your way also.

I reject all deals | 2023-05-25 20:24

Hmmm… Yeah, this is probably related to the comment I made above…

handling binary data as a “string” may lead to unexpected results

When I try to load an image file and convert the underling PoolByteArray to a string-rep, I’m getting a Unicode error: invalid skip error.

It’s not clear to me whether that’s a potential Godot bug or whether the conversion to UTF-8 encoding (what Godot is attempting to do) just doesn’t make any sense.

You can definitely load and save the image data cleanly in Godot. But, again, I’m not sure what you’re trying to do with the raw binary data by “displaying” it. To display the data in any text-based control (Godot UI or otherwise), it needs to be coerced into something it’s not. So, even if we found a way to “display” the data, I don’t think you could actually do anything useful with it in that form.

So, again I ask - what is the purpose of what you’re trying to do? Why do you want to display the raw binary data? Knowing that, maybe there’s a better way to get to your end-goal…

jgodfrey | 2023-05-25 23:57

I want to do very simple hide text in image.
Just getting image data as text, and adding some string to end of it.
And saving it again as an image.

I reject all deals | 2023-05-27 16:02

I want to do very simple hide text in image.

A few comments…

  • Do you know for sure that adding text to the end of your image (whatever image form that is) won’t break the image decoding? Most image files (really, binary files in general) use an encoded set of pointers to access different sections of data in the binary blob. For example, with a JPG image, there’s the actual image scan data, plus all kinds of potential meta-data (EXIF, IPTC, XMP, …). Just randomly adding text-based content to such a file will likely break existing image readers / decoders. Unless you’re very familiar with the binary format you’re interacting with and know for sure the edits won’t break the file that sounds dangerous. I suspect it WILL break the file…

  • If you really can add text-data to the end of the file you’re considering without breaking it, I’d recommend a different approach. Rather than attempting to edit the RAW binary data, why not just collect the text data independently (using standard text-input controls) and then just append that text data to the image file. There’s really no need to interact directly with the binary contents of the file.

jgodfrey | 2023-05-27 16:13

Yes the sound is great but how can I do it?
I mean to say how to append the text to image in godot?

I reject all deals | 2023-05-28 15:08

Here’s a quick example…

func _ready():
	var img = "/path/to/your/img.jpg"
	var file = File.new()
	file.open(img, File.READ_WRITE)
	file.seek(file.get_len())
	file.store_pascal_string("The string to store")
	file.close()

That’ll open the specified image, move the file pointer to the end of existing file data, append a new string to the end, and close the file.

jgodfrey | 2023-05-28 18:40

Thank you so much!
That is what I am looking for it.
But how I will get the text from image again?
For example for knowing the messages place I will add a key for example one for begin “message_starts_here” and one for and “message_ends_here” maybe no need to add for end. Now how I will get the my hidden text?

I reject all deals | 2023-05-28 19:57

Retrieval depends a lot on the details of what you’re doing. Are you adding just a single string? Are you updating the file multiple times? Do you need to find a specific string (from several added string)?

Again, none of this seems ideal to me. Assuming you know the original file length, you can easily retrieve single string added by modifying the above code to something like this:

func _ready():
	var img = "/home/jeff/Desktop/img.jpg"
	var file = File.new()
	file.open(img, File.READ_WRITE)
	var l = file.get_len()
	file.seek(l)
	file.store_pascal_string("The string to store")
	file.close()
	file.open(img, File.READ)
	file.seek(l)
	var s = file.get_pascal_string()
	file.close()
	print(s)

That’s the same write code. However, now the code remembers the original file length (prior to string insertion). Then, to read the string back, it opens the file in READ mode, advances the read cursor to the original end of the file (so, now the added string’s start position), and retrieves the string via get_pascal_string().

This is obviously quite simple when the write and read are done sequentially as above. However, it’s unlikely that you’ll know the file’s original length when you want to restore the string. In that case, you’d have to devise some other method of locating the string. You could maybe search for some “tag” that identifies the string start (as you suggest above), or write the inserted string’s length at the end of the file, so you know how far to move the read pointer to grab the string, or something else entirely…

jgodfrey | 2023-05-28 20:26

Related… It looks like the store_pascal_string() method writes the string length to the file (as 4 byte sequence) just prior to adding the string itself. For my test message above, that byte sequence looks like this:

13 00 00 00

That’s the string’s length of 19 chars (hex 13 = dec 19).

To retrieve the string, you have to put the file read pointer on that first byte (13 above) and then call get_pascal_string(). You might be able to use that 4-byte signature to your advantage in locating a previously added string.

For example, you might do something like:

  • Open the file in read mode
  • Move the pointer to the end of the file (via seek)
  • Read each byte in reverse order until you hit a null byte (00).
  • In theory, that’d be the last of the 4-byte sequence I show above (though, with a long enough string, it wouldn’t be null presumably, so this is somewhat brittle).
  • Once found, you could back up 3 more bytes to put the read pointer on the first length byte
  • Then, call get_pascal_string() to retrieve the stored string.

Again, not ideal, but would likely work for common use cases.

jgodfrey | 2023-05-28 20:42