Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write channel id and name to display settings nodes #106

Merged
merged 5 commits into from
Aug 19, 2024
Merged
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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # enable new "MSVC runtime library selection" (https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html)

project(libCZI
VERSION 0.59.0
VERSION 0.60.0
HOMEPAGE_URL "https://github.com/ZEISS/libczi"
DESCRIPTION "libCZI is an Open Source Cross-Platform C++ library to read and write CZI")

Expand Down
114 changes: 102 additions & 12 deletions Src/libCZI/CziMetadataBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,20 @@
attribute.set_value(Utilities::convertUtf8ToWchar_t(value).c_str());
}

/*virtual*/void CNodeWrapper::SetAttribute(const wchar_t* name, const wchar_t* value)
{
auto attribute = this->node.attribute(name);

// if the attribute was already existing, we use (and modify) it
if (!attribute)
{
// otherwise, we add a new attribute
attribute = this->node.append_attribute(name);
}

attribute.set_value(value);
}

/*static*/pugi::xml_node CNodeWrapper::GetOrCreateChildElementNodeWithAttributes(pugi::xml_node& node, const std::wstring& str)
{
std::wregex nodenameWihtAttribregex(LR"(([^\[\]]+)(\[([^\[\]]*)\])?)");
Expand Down Expand Up @@ -504,7 +518,7 @@
libCZI::ICziMetadataBuilder* builder,
const libCZI::SubBlockStatistics& statistics,
const PixelTypeForChannelIndexStatistic& pixelTypeForChannel,
std::function<std::tuple<std::string, std::tuple<bool, std::string>>(int channelIdx)> getIdAndName)
const std::function<std::tuple<std::string, std::tuple<bool, std::string>>(int channelIdx)>& getIdAndName)
{
int cStartIdx, cIdxSize;
if (statistics.dimBounds.TryGetInterval(DimensionIndex::C, &cStartIdx, &cIdxSize))
Expand All @@ -525,7 +539,7 @@
libCZI::ICziMetadataBuilder* builder,
int channelIdxStart, int channelIdxSize,
const PixelTypeForChannelIndexStatistic& pixelTypeForChannel,
std::function<std::tuple<std::string, std::tuple<bool, std::string>>(int channelIdx)> getIdAndName)
const std::function<std::tuple<std::string, std::tuple<bool, std::string>>(int channelIdx)>& getIdAndName)
{
auto root = builder->GetRootNode();

Expand Down Expand Up @@ -877,14 +891,14 @@
/*static*/void libCZI::MetadataUtils::WriteGeneralDocumentInfo(ICziMetadataBuilder* builder, const libCZI::GeneralDocumentInfo& info)
{
const auto setField = [=](bool isValid, const char* nodeName, const wstring& s)
{
if (isValid)
{
string n("Metadata/Information/Document/");
n.append(nodeName);
builder->GetRootNode()->GetOrCreateChildNode(n.c_str())->SetValue(s.c_str());
}
};
if (isValid)
{
string n("Metadata/Information/Document/");
n.append(nodeName);
builder->GetRootNode()->GetOrCreateChildNode(n.c_str())->SetValue(s.c_str());
}
};

setField(info.name_valid, "Name", info.name);
setField(info.title_valid, "Title", info.title);
Expand Down Expand Up @@ -1016,7 +1030,48 @@
}
}

static bool TryRetrieveIdAndNameAttributeFromChannels(IXmlNodeRw* root, int channelNo, wstring& channelId, wstring& channelName)
{
stringstream ss;
ss << "Metadata/Information/Image/Dimensions/Channels/Channel[" << channelNo << "]";
auto channelNode = root->GetChildNodeReadonly(ss.str().c_str());
if (channelNode)
{
const bool idAttributeFound = channelNode->TryGetAttribute(L"Id", &channelId);
if (!idAttributeFound)
{
channelId.clear();

Check warning on line 1043 in Src/libCZI/CziMetadataBuilder.cpp

View check run for this annotation

Codecov / codecov/patch

Src/libCZI/CziMetadataBuilder.cpp#L1043

Added line #L1043 was not covered by tests
}

const bool nameAttributeFound = channelNode->TryGetAttribute(L"Name", &channelName);
if (!nameAttributeFound)
{
channelName.clear();
}

return idAttributeFound || nameAttributeFound;
}

return false;
}

/*static*/void libCZI::MetadataUtils::WriteDisplaySettings(libCZI::ICziMetadataBuilder* builder, const libCZI::IDisplaySettings* display_settings, int channel_count, const std::map<int, PixelType>* channel_pixel_type)
{
MetadataUtils::WriteDisplaySettings(
builder,
display_settings,
channel_count,
[=](int channelIdx, CoerceAdditionalInfoForChannelDisplaySettings& coerceAdditionalInfo)->void
{
if (channel_pixel_type != nullptr && channel_pixel_type->find(channelIdx) != channel_pixel_type->end())
{
coerceAdditionalInfo.pixelType = channel_pixel_type->at(channelIdx);
coerceAdditionalInfo.writePixelType = true;
}
});
}

/*static*/void libCZI::MetadataUtils::WriteDisplaySettings(libCZI::ICziMetadataBuilder* builder, const libCZI::IDisplaySettings* display_settings, int channel_count, const std::function<void(int, CoerceAdditionalInfoForChannelDisplaySettings&)>& coerce_additional_info_functor)
{
const auto display_settings_channel_node = builder->GetRootNode()->GetOrCreateChildNode("Metadata/DisplaySetting/Channels");

Expand All @@ -1031,11 +1086,46 @@
auto channel_node = display_settings_channel_node->AppendChildNode("Channel");
if (channel_display_settings)
{
CoerceAdditionalInfoForChannelDisplaySettings coerce_additional_info;

// Try to retrieve the attributes "Id" and "Name" from the corresponding dimension-channel-node, and if successful, use those values
// for the attributes of the current channel-node in the display-settings-data.
wstring channelId, channelName;
if (TryRetrieveIdAndNameAttributeFromChannels(builder->GetRootNode().get(), c, channelId, channelName))
{
if (!channelId.empty())
{
coerce_additional_info.idAttribute = channelId;
coerce_additional_info.writeIdAttribute = true;
}

if (!channelName.empty())
{
coerce_additional_info.nameAttribute = channelName;
coerce_additional_info.writeNameAttribute = true;
}
}

// if non-null, call the functor which may change the "additional information" we want to write into the XML
if (coerce_additional_info_functor)
{
coerce_additional_info_functor(c, coerce_additional_info);
}

string pixel_type_string;
if (channel_pixel_type != nullptr && channel_pixel_type->find(c) != channel_pixel_type->end())
if (coerce_additional_info.writePixelType)
{
CMetadataPrepareHelper::TryConvertToXmlMetadataPixelTypeString(coerce_additional_info.pixelType, pixel_type_string);
}

if (coerce_additional_info.writeIdAttribute)
{
channel_node->SetAttribute(L"Id", coerce_additional_info.idAttribute.c_str());
}

if (coerce_additional_info.writeNameAttribute)
{
const PixelType pixel_type = channel_pixel_type->at(c);
CMetadataPrepareHelper::TryConvertToXmlMetadataPixelTypeString(pixel_type, pixel_type_string);
channel_node->SetAttribute(L"Name", coerce_additional_info.nameAttribute.c_str());
}

WriteChannelDisplaySettings(channel_display_settings.get(), channel_node.get(), pixel_type_string);
Expand Down
5 changes: 3 additions & 2 deletions Src/libCZI/CziMetadataBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class CNodeWrapper : public libCZI::IXmlNodeRw
std::shared_ptr<IXmlNodeRw> GetOrCreateChildNode(const char* path) override;
std::shared_ptr<IXmlNodeRw> GetChildNode(const char* path) override;
void SetAttribute(const char* name, const char* value) override;
void SetAttribute(const wchar_t* name, const wchar_t* value) override;
void SetValue(const char* str) override;
void SetValue(const wchar_t* str) override;
void SetValueI32(int value) override;
Expand Down Expand Up @@ -99,14 +100,14 @@ class CMetadataPrepareHelper
libCZI::ICziMetadataBuilder* builder,
const libCZI::SubBlockStatistics& statistics,
const PixelTypeForChannelIndexStatistic& pixelTypeForChannel,
std::function<std::tuple<std::string, std::tuple<bool, std::string>>(int channelIdx)> getIdAndName
const std::function<std::tuple<std::string, std::tuple<bool, std::string>>(int channelIdx)>& getIdAndName
);

static void FillDimensionChannel(
libCZI::ICziMetadataBuilder* builder,
int channelIdxStart, int channelIdxSize,
const PixelTypeForChannelIndexStatistic& pixelTypeForChannel,
std::function<std::tuple<std::string, std::tuple<bool, std::string>>(int channelIdx)> getIdAndName);
const std::function<std::tuple<std::string, std::tuple<bool, std::string>>(int channelIdx)>& getIdAndName);

static bool TryConvertToXmlMetadataPixelTypeString(libCZI::PixelType pxlType, std::string& str);

Expand Down
4 changes: 2 additions & 2 deletions Src/libCZI/CziWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@ void CCziWriter::WriteSubBlock(const libCZI::AddSubBlockInfo& addSbBlkInfo)
{
CWriterUtils::WriteInfo writeInfo;
writeInfo.segmentPos = this->nextSegmentPos;
writeInfo.writeFunc = std::bind(&CCziWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5); ;
writeInfo.writeFunc = std::bind(&CCziWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5);
writeInfo.useSpecifiedAllocatedSize = false;
this->nextSegmentPos += CWriterUtils::WriteSubBlock(writeInfo, addSbBlkInfo);
}
Expand Down Expand Up @@ -1117,7 +1117,7 @@ void CCziWriter::WriteAttachment(const libCZI::AddAttachmentInfo& addAttachmentI
{
CWriterUtils::WriteInfo writeInfo;
writeInfo.segmentPos = this->nextSegmentPos;
writeInfo.writeFunc = std::bind(&CCziWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5); ;
writeInfo.writeFunc = std::bind(&CCziWriter::WriteToOutputStream, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4, placeholders::_5);
writeInfo.useSpecifiedAllocatedSize = false;
this->nextSegmentPos += CWriterUtils::WriteAttachment(writeInfo, addAttachmentInfo);
}
Expand Down
3 changes: 2 additions & 1 deletion Src/libCZI/Doc/version-history.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ version history {#version_history}
0.58.2 | [96](https://github.com/ZEISS/libczi/pull/96) | small fixes for deficiencies reported by CodeQL
0.58.3 | [97](https://github.com/ZEISS/libczi/pull/97) | update zstd to [version 1.5.6](https://github.com/facebook/zstd/releases/tag/v1.5.6)
0.58.4 | [99](https://github.com/ZEISS/libczi/pull/99) | fix a rare issue with curl_http_inputstream which would fail to read CZIs with an attachment-directory containing zero entries
0.59.0 | [99](https://github.com/ZEISS/libczi/pull/103) | add a check for physical size for dimensions other than X,Y,M, they must not be >1, active for strict parsing.
0.59.0 | [99](https://github.com/ZEISS/libczi/pull/103) | add a check for physical size for dimensions other than X,Y,M, they must not be >1, active for strict parsing.
0.60.0 | [106](https://github.com/ZEISS/libczi/pull/106) | with metadata-builder, by default copy the attributes "Id" and "Name" from the channel-node; allow to control the behavior fine-grained
33 changes: 31 additions & 2 deletions Src/libCZI/libCZI_Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ namespace libCZI
LookUpTableExplicit = 2, ///< (NOT YET IMPLEMENTED) There is an explicit look-up-table specified.
LookUpTableWellKnown = 3 ///< (NOT YET IMPLEMENTED) We are using a "well-known" look-up-table, and it is identified by its name (which is a string).
};

/// The coefficients of a cubic spline defined by \f$a\,x^3 + b\,x^2 + c\,x + d =y\f$.
struct CubicSplineCoefficients
{
Expand Down Expand Up @@ -602,7 +602,7 @@ namespace libCZI
///
/// \param [in,out] data If non-null, the spline data will be written to this vector.
///
/// \return True if it the corresponding channels uses gradation curve mode <tt>Spline</tt>, false otherwise.
/// \return True if the corresponding channels uses gradation curve mode <tt>Spline</tt>, false otherwise.
virtual bool TryGetSplineData(std::vector<libCZI::IDisplaySettings::SplineData>* data) const = 0;

virtual ~IChannelDisplaySetting() {}
Expand Down Expand Up @@ -968,6 +968,12 @@ namespace libCZI
/// \param value The value (as UTF8-encoded string).
virtual void SetAttribute(const char* name, const char* value) = 0;

/// Sets the attribute with the specified name to the specified value.
///
/// \param name The name of the attribute.
/// \param value The value.
virtual void SetAttribute(const wchar_t* name, const wchar_t* value) = 0;

/// Sets the node value - which is specified as an UTF8-string.
///
/// \param str The UTF8-encoded string.
Expand Down Expand Up @@ -1274,6 +1280,17 @@ namespace libCZI
class LIBCZI_API MetadataUtils
{
public:
/// This struct is used for controlling the operation of adding display-settings.
struct CoerceAdditionalInfoForChannelDisplaySettings
{
bool writePixelType{ false }; ///< Whether to write an element "PixelType".
libCZI::PixelType pixelType; ///< The pixel-type to write to the element "PixelType" (if "writePixelType" is true).
bool writeIdAttribute{ false }; ///< Whether to write an attribute "Id".
std::wstring idAttribute; ///< The value of the attribute "Id" (if "writeIdAttribute" is true).
bool writeNameAttribute{ false }; ///< Whether to write an attribute "Name".
std::wstring nameAttribute; ///< The value of the attribute "Name" (if "writeNameAttribute" is true).
};

/// Writes the nodes ""Metadata/Information/Image/SizeX" and ""Metadata/Information/Image/SizeY".
///
/// \param [in,out] builder The metadata-builder object.
Expand Down Expand Up @@ -1387,6 +1404,18 @@ namespace libCZI
/// \param channel_count The number of channels (which are constructed in the display-settings XML-metadata).
/// \param channel_pixel_type The map of the channel and its corresponding pixel type.
static void WriteDisplaySettings(libCZI::ICziMetadataBuilder* builder, const libCZI::IDisplaySettings* display_settings, int channel_count, const std::map<int, PixelType>* channel_pixel_type = nullptr);

/// Helper function which writes the specified display-settings into the specified metadata-builder. The display-settings
/// XML-metadata-node will have as many channel-items as specified with the argument 'channel_count'.
/// If there are nodes with name "Channel" existing (prior to calling this function) under the node
/// "Metadata/DisplaySetting/Channels", they are removed (before adding new content).
/// This function allows to coerce and control some additional information that is written to the XML-metadata-node.
///
/// \param [in,out] builder The metadata-builder object.
/// \param display_settings The display settings.
/// \param channel_count The number of channels (which are constructed in the display-settings XML-metadata).
/// \param coerce_additional_info_functor A functor which allows to mutate and/or control what additional information is written to the XML-metadata-node.
static void WriteDisplaySettings(libCZI::ICziMetadataBuilder* builder, const libCZI::IDisplaySettings* display_settings, int channel_count, const std::function<void(int, CoerceAdditionalInfoForChannelDisplaySettings&)>& coerce_additional_info_functor);
};
}

Loading
Loading