Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions src/engraving/dom/imageStore.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class ImageStoreItem
bool isUsed() const { return !m_references.empty(); }
std::string hashName() const;
const muse::ByteArray& hash() const { return m_hash; }
const std::string& type() const { return m_type; }
void set(const muse::ByteArray& b, const muse::ByteArray& h) { m_buffer = b; m_hash = h; }

private:
Expand Down
117 changes: 116 additions & 1 deletion src/importexport/musicxml/internal/export/exportmusicxml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
#include "engraving/dom/hammeronpulloff.h"
#include "engraving/dom/harmonicmark.h"
#include "engraving/dom/harmony.h"
#include "engraving/dom/image.h"
#include "engraving/dom/imageStore.h"
#include "engraving/dom/harppedaldiagram.h"
#include "engraving/dom/instrchange.h"
#include "engraving/dom/jump.h"
Expand Down Expand Up @@ -347,6 +349,7 @@ class ExportMusicXml : public muse::Contextable
void moveToTickIfNeed(const Fraction& t, track_idx_t track, const Fraction& measureTick);
void words(TextBase const* const text, staff_idx_t staff);
void tboxTextAsWords(TextBase const* const text, const staff_idx_t staff, PointF position);
void image(const Image* const img, staff_idx_t staff);
void rehearsal(RehearsalMark const* const rmk, staff_idx_t staff);
void harpPedals(HarpPedalDiagram const* const hpd, staff_idx_t staff);
void hairpin(Hairpin const* const hp, staff_idx_t staff, const Fraction& tick);
Expand Down Expand Up @@ -5111,7 +5114,7 @@ void ExportMusicXml::words(TextBase const* const text, staff_idx_t staff)
muPrintable(text->plainText()));
*/

if (text->plainText() == "") {
if (text->plainText() == u"") {
Copy link
Copy Markdown
Contributor

@Jojo-Schmitz Jojo-Schmitz May 22, 2026

Choose a reason for hiding this comment

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

Why not if (text->plainText().isEmpty()) {?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There were more cases, so I unified them all.

// sometimes empty Texts are present, exporting would result
// in invalid MusicXML (as an empty direction-type would be created)
return;
Expand All @@ -5123,6 +5126,107 @@ void ExportMusicXml::words(TextBase const* const text, staff_idx_t staff)
directionETag(m_xml, staff);
}

//---------------------------------------------------------
// getImageInfo
//---------------------------------------------------------

static void getImageInfo(const ImageStoreItem* isi, String& source, String& type)
{
source = String::fromStdString(isi->hashName());
String suffix = String::fromStdString(isi->type()).toLower();

if (suffix == u"svg" || suffix == u"svgz") {
type = u"image/svg+xml";
} else if (suffix == u"png") {
type = u"image/png";
} else if (suffix == u"jpg" || suffix == u"jpeg") {
type = u"image/jpeg";
} else if (suffix == u"gif") {
type = u"image/gif";
} else if (suffix == u"bmp") {
type = u"image/bmp";
} else if (suffix == u"tif" || suffix == u"tiff") {
type = u"image/tiff";
}

if (type.isEmpty()) {
const ByteArray& ba = isi->buffer();
if (ba.size() >= 4) {
if (ba.at(0) == 0x89 && ba.at(1) == 0x50 && ba.at(2) == 0x4E && ba.at(3) == 0x47) {
type = u"image/png";
if (suffix.isEmpty()) {
source += u".png";
}
} else if (ba.at(0) == 0xFF && ba.at(1) == 0xD8 && ba.at(2) == 0xFF) {
type = u"image/jpeg";
if (suffix.isEmpty()) {
source += u".jpg";
}
} else if (ba.at(0) == 0x47 && ba.at(1) == 0x49 && ba.at(2) == 0x46 && ba.at(3) == 0x38) {
type = u"image/gif";
if (suffix.isEmpty()) {
source += u".gif";
}
} else if (ba.at(0) == 0x42 && ba.at(1) == 0x4D) {
type = u"image/bmp";
if (suffix.isEmpty()) {
source += u".bmp";
}
} else if (ba.at(0) == 0x3C && (ba.at(1) == 0x3F || ba.at(1) == 0x73)) {
type = u"image/svg+xml";
if (suffix.isEmpty()) {
source += u".svg";
}
}
}
}

if (type.isEmpty()) {
type = u"application/octet-stream";
}
}

//---------------------------------------------------------
// image
//---------------------------------------------------------

void ExportMusicXml::image(const Image* const img, staff_idx_t staff)
{
ImageStoreItem* isi = img->storeItem();
if (!isi) {
return;
}

directionTag(m_xml, m_attr, img);
m_xml.startElement("direction-type");

String source;
String type;
getImageInfo(isi, source, type);

String imgTag = u"image source=\"" + XmlWriter::xmlString(source) + u"\"";
imgTag += u" type=\"" + XmlWriter::xmlString(type) + u"\"";

double width = img->imageWidth();
double height = img->imageHeight();
if (!img->sizeIsSpatium()) {
double sp = img->spatium();
if (sp > 0.0) {
width /= sp;
height /= sp;
}
}

imgTag += u" height=\"" + String::number(height * 10.0, 2) + u"\"";
imgTag += u" width=\"" + String::number(width * 10.0, 2) + u"\"";
imgTag += positioningAttributes(img);

m_xml.tagRaw(imgTag);
m_xml.endElement(); // direction-type

directionETag(m_xml, staff);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

//---------------------------------------------------------
// systemText
//---------------------------------------------------------
Expand Down Expand Up @@ -6525,6 +6629,8 @@ static bool commonAnnotations(ExportMusicXml* exp, const EngravingItem* e, staff
exp->harpPedals(toHarpPedalDiagram(e), sstaff);
} else if (e->isRehearsalMark()) {
exp->rehearsal(toRehearsalMark(e), sstaff);
} else if (e->isImage()) {
exp->image(toImage(e), sstaff);
} else if (e->isSystemText()) {
exp->systemText(toStaffTextBase(e), sstaff);
}
Expand Down Expand Up @@ -8850,6 +8956,15 @@ static void writeMxlArchive(Score* score, muse::ZipWriter& zip, const String& fi
em.write(&dbuf);
dbuf.seek(0);
zip.addFile(filename.toStdString(), dbuf.data());

for (const ImageStoreItem* isi : imageStore) {
if (isi->isUsed(score)) {
String source;
String type;
getImageInfo(isi, source, type);
zip.addFile(source.toStdString(), isi->buffer());
}
}
}

bool saveMxl(Score* score, IODevice* device)
Expand Down
Loading