Skip to content

Conversation

@N-Coder
Copy link
Contributor

@N-Coder N-Coder commented Oct 27, 2024

This is a first step towards external image support, see also #545.
I think we can copy most of the UX here from the inkscape SVG editor, which already has a very similar functionality:

ToDo:

  • read image data from external file instead of base64 data in XML when encoding is "externalPath"
  • check that paths are always resolved relatively to ipe file (which is not necessarily the cwd?)
  • do not change cwd, but store a per-document relative base path instead
  • use path info instead of image data when writing XML (may need special treatment for PDF)
  • update paths when saving in a different directory
  • use replacement image buffer when image could not be loaded
  • reload external images when running latex (or only do so if their timestamps changed)
  • check handling of links when autosaving to different (central) directory
  • UI: allow user to choose between embedding and (relative/absolute) linking when inserting images
  • UI: allow converting present images between embedded and linked in right-click context menu
  • UI: find way of handling / resolving external images that cannot be loaded (e.g. search again with a base path different from current working dir, allow giving absolute paths to replacements,...)
  • UI: ask whether we should use embedded image data when path from just-loaded PDF cannot be resolved (same for reloading external images that suddenly no longer exist)
  • allow "temporarily" storing image data externally for quicker autosaving and on ipe-web

@N-Coder N-Coder changed the title support reading externally linked (instead of embedded) images support externally linked (instead of embedded) images Oct 27, 2024
@otfried
Copy link
Owner

otfried commented Oct 27, 2024

Perhaps we should discuss if it is worth adding this complexity to Ipe before we proceed - because, strictly speaking, it does not add new functionality.

You can already store your images externally by making a text label with contents

\includegraphics{myimage}

This could be made more comfortable by having the "import image" function create the text object, and we could have a function (or perhaps an ipelet) that externalizes an image that is currently internal.

What can be done with encoding="externalPath" that cannot also be done with \includegraphics ?

@N-Coder N-Coder marked this pull request as draft October 27, 2024 10:51
@N-Coder
Copy link
Contributor Author

N-Coder commented Oct 27, 2024

You can already store your images externally by making a text label with contents \includegraphics{myimage}

That is a very good point, as it actually also allows to include PDF pages and all the other arguments of includegraphics.
The downside is that the free-text LaTeX label is very hard to work with programatically. It is harder to catch errors due to images that have moved and much harder to automatically update all their paths, especially if a label contains some additional LaTeX code.

This could be made more comfortable by having the "import image" function create the text object, and we could have a function (or perhaps an ipelet) that externalizes an image that is currently internal.

That sounds like a very good idea!

What can be done with encoding="externalPath" that cannot also be done with \includegraphics ?

The only advantage would be that it is much easier to edit programatically, but I do see that throwing around more weirdly-encoded byte buffers is not that nice.

So maybe let's take one step back and first think about what we actually want feature- and UX-wise and then think how we want to achieve this through library C++ / ipelet lua code and a maybe updated XML data representation. I'll try to give my 5 cents on this in the linked issue some time later today.

@otfried
Copy link
Owner

otfried commented Oct 28, 2024

Please change it so there is an additional attribute path on <bitmap> objects whose value is the file path. The contents of the <bitmap> element should simply be empty, and encoding should not be used.

This is more in line with how <bitmap> elements look inside PDF files, and like those, they should be saved in the short format

<bitmap id="27" path="myfigure.png" width="720" height="360" />

@otfried otfried force-pushed the master branch 8 times, most recently from bc4cb29 to c8dd95f Compare November 3, 2024 21:49
}
}

void Bitmap::changeExternalPathRelativeBase(String new_base) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep the code base consistent, I'd prefer camelCase: newBase instead of new_base.


void Bitmap::changeExternalPathRelativeBase(String new_base) {
new_base = Platform::realPath(new_base);
String old_path = Platform::realPath(externalPath());
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we start using <filesystem>, should we just switch to use std::filesystem::absolute instead of Platform::realPath here?

It seems std::filesystem can relieve Ipe of the burden of all the special handling for Windows (where filenames have to be in wchar_t and the separator is a backslash).

new_base = Platform::realPath(new_base);
String old_path = Platform::realPath(externalPath());
setExternalPath(
std::filesystem::proximate(old_path.s(), new_base.s()).generic_string());
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it should be generic_u8string. In Ipe, all strings are in UTF-8, even if that is not the computer's local encoding, and on Windows it will not work like this.

reason = EFileOpenError;
std::FILE * fd = Platform::fopen(fname, "rb");
if (!fd) return nullptr;
Platform::changeDirectory(Platform::parentDirectory(fname));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot simply change the current directory inside Ipe.
There are quite a few people who have ipelets that call bash scripts or that expect the cwd to be in a specific place for some other reason.

We would at least have to set it back after the bitmaps have been loaded.

However, this also seems the wrong place. Just reading the document using Document::load should definitely not load all the bitmaps - imagine this being called from an ipescript that just wants to extract some information.

Loading the bitmaps should only happen when we need to render them.

if (getcwd(buffer, 1024) != buffer) return String();
return String(buffer);
#endif
return std::filesystem::current_path().generic_string();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generic_u8string

I'm tempted to say we get rid of Platform::currentDirectory and just call std::filesystem directly in the code, but that's something I can look at later.

@otfried otfried force-pushed the master branch 2 times, most recently from d0b335f to a98414e Compare August 2, 2025 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants