LCOV - code coverage report
Current view: top level - src/converter/internal/compat - backendapi.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 473 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "backendapi.h"
      23              : 
      24              : #include <stdio.h>
      25              : 
      26              : #include <QString>
      27              : #include <QJsonDocument>
      28              : #include <QJsonObject>
      29              : #include <QJsonArray>
      30              : #include <QJsonValue>
      31              : #include <QRandomGenerator>
      32              : 
      33              : #include "io/buffer.h"
      34              : 
      35              : #include "engraving/infrastructure/mscwriter.h"
      36              : #include "engraving/dom/excerpt.h"
      37              : #include "engraving/dom/score.h"
      38              : #include "engraving/rw/mscsaver.h"
      39              : #include "engraving/types/typesconv.h"
      40              : 
      41              : #include "notation/iexcerptnotation.h" // IWYU pragma: keep
      42              : #include "notation/imasternotation.h"
      43              : #include "notation/inotationelements.h" // IWYU pragma: keep
      44              : 
      45              : #include "project/types/projectmeta.h"
      46              : 
      47              : #include "internal/converterutils.h"
      48              : 
      49              : #include "backendjsonwriter.h"
      50              : #include "notationmeta.h"
      51              : #include "scoreelementsscanner.h"
      52              : 
      53              : #include "log.h"
      54              : 
      55              : using namespace mu;
      56              : using namespace mu::converter;
      57              : using namespace mu::project;
      58              : using namespace mu::notation;
      59              : using namespace mu::engraving;
      60              : using namespace muse;
      61              : using namespace muse::io;
      62              : 
      63              : static const std::string PNG_WRITER_NAME = "png";
      64              : static const std::string SVG_WRITER_NAME = "svg";
      65              : static const std::string SEGMENTS_POSITIONS_WRITER_NAME = "spos";
      66              : static const std::string SEGMENTS_POSITIONS_TAG_NAME = "sposXML";
      67              : static const std::string MEASURES_POSITIONS_WRITER_NAME = "mpos";
      68              : static const std::string MEASURES_POSITIONS_TAG_NAME = "mposXML";
      69              : static const std::string PDF_WRITER_NAME = "pdf";
      70              : static const std::string MIDI_WRITER_NAME = "midi";
      71              : static const std::string MUSICXML_WRITER_NAME = "mxl";
      72              : static const std::string MUSICXML_JSON_NAME = "mxml";
      73              : static const std::string META_DATA_NAME = "metadata";
      74              : static const std::string DEV_INFO_NAME = "devinfo";
      75              : 
      76              : static constexpr bool ADD_SEPARATOR = true;
      77              : 
      78            0 : Ret BackendApi::exportScoreMedia(const muse::modularity::ContextPtr& iocCtx, const muse::io::path_t& in, const muse::io::path_t& out,
      79              :                                  const muse::io::path_t& highlightConfigPath, const OpenParams& openParams)
      80              : {
      81            0 :     TRACEFUNC
      82              : 
      83            0 :     RetVal<INotationProjectPtr> prj = openProject(iocCtx, in, openParams);
      84            0 :     if (!prj.ret) {
      85            0 :         return prj.ret;
      86              :     }
      87              : 
      88            0 :     INotationPtr notation = prj.val->masterNotation()->notation();
      89              : 
      90            0 :     bool result = true;
      91              : 
      92            0 :     QFile outputFile;
      93            0 :     openOutputFile(outputFile, out);
      94              : 
      95            0 :     BackendJsonWriter jsonWriter(&outputFile);
      96              : 
      97            0 :     result &= exportScorePngs(notation, jsonWriter, ADD_SEPARATOR);
      98            0 :     result &= exportScoreSvgs(notation, highlightConfigPath, jsonWriter, ADD_SEPARATOR);
      99            0 :     result &= exportScoreElementsPositions(SEGMENTS_POSITIONS_WRITER_NAME, SEGMENTS_POSITIONS_TAG_NAME,
     100            0 :                                            notation, jsonWriter, ADD_SEPARATOR);
     101            0 :     result &= exportScoreElementsPositions(MEASURES_POSITIONS_WRITER_NAME, MEASURES_POSITIONS_TAG_NAME,
     102            0 :                                            notation, jsonWriter, ADD_SEPARATOR);
     103            0 :     result &= exportScorePdf(notation, jsonWriter, ADD_SEPARATOR);
     104            0 :     result &= exportScoreMidi(notation, jsonWriter, ADD_SEPARATOR);
     105            0 :     result &= exportScoreMusicXML(notation, jsonWriter, ADD_SEPARATOR);
     106            0 :     result &= exportScoreMetaData(notation, jsonWriter, ADD_SEPARATOR);
     107            0 :     result &= devInfo(notation, jsonWriter);
     108              : 
     109            0 :     return result ? make_ret(Ret::Code::Ok) : make_ret(Ret::Code::InternalError);
     110            0 : }
     111              : 
     112            0 : Ret BackendApi::exportScoreMeta(const muse::modularity::ContextPtr& iocCtx, const muse::io::path_t& in, const muse::io::path_t& out,
     113              :                                 const OpenParams& openParams)
     114              : {
     115            0 :     TRACEFUNC
     116              : 
     117            0 :     RetVal<INotationProjectPtr> prj = openProject(iocCtx, in, openParams);
     118            0 :     if (!prj.ret) {
     119            0 :         return prj.ret;
     120              :     }
     121              : 
     122            0 :     INotationPtr notation = prj.val->masterNotation()->notation();
     123              : 
     124            0 :     QFile outputFile;
     125            0 :     openOutputFile(outputFile, out);
     126              : 
     127            0 :     BackendJsonWriter jsonWriter(&outputFile);
     128              : 
     129            0 :     bool result = exportScoreMetaData(notation, jsonWriter);
     130              : 
     131            0 :     return result ? make_ret(Ret::Code::Ok) : make_ret(Ret::Code::InternalError);
     132            0 : }
     133              : 
     134            0 : Ret BackendApi::exportScoreParts(const muse::modularity::ContextPtr& iocCtx, const muse::io::path_t& in, const muse::io::path_t& out,
     135              :                                  const OpenParams& openParams)
     136              : {
     137            0 :     TRACEFUNC
     138              : 
     139            0 :     RetVal<INotationProjectPtr> prj = openProject(iocCtx, in, openParams);
     140            0 :     if (!prj.ret) {
     141            0 :         return prj.ret;
     142              :     }
     143              : 
     144            0 :     QFile outputFile;
     145            0 :     openOutputFile(outputFile, out);
     146              : 
     147            0 :     Ret ret = doExportScoreParts(iocCtx, prj.val->masterNotation(), outputFile);
     148              : 
     149            0 :     outputFile.close();
     150              : 
     151            0 :     return ret;
     152            0 : }
     153              : 
     154            0 : Ret BackendApi::exportScorePartsPdfs(const muse::modularity::ContextPtr& iocCtx, const muse::io::path_t& in, const muse::io::path_t& out,
     155              :                                      const OpenParams& openParams)
     156              : {
     157            0 :     TRACEFUNC
     158              : 
     159            0 :     RetVal<INotationProjectPtr> prj = openProject(iocCtx, in, openParams);
     160            0 :     if (!prj.ret) {
     161            0 :         return prj.ret;
     162              :     }
     163              : 
     164            0 :     QFile outputFile;
     165            0 :     openOutputFile(outputFile, out);
     166              : 
     167            0 :     std::string scoreFileName = io::dirpath(in).toStdString() + "/" + io::filename(in, false).toStdString() + ".pdf";
     168              : 
     169            0 :     Ret ret = doExportScorePartsPdfs(prj.val->masterNotation(), outputFile, scoreFileName);
     170              : 
     171            0 :     outputFile.close();
     172              : 
     173            0 :     return ret;
     174            0 : }
     175              : 
     176            0 : Ret BackendApi::exportScoreTranspose(const muse::modularity::ContextPtr& iocCtx, const muse::io::path_t& in, const muse::io::path_t& out,
     177              :                                      const std::string& optionsJson, const OpenParams& openParams)
     178              : {
     179            0 :     TRACEFUNC
     180              : 
     181            0 :     RetVal<INotationProjectPtr> prj = openProject(iocCtx, in, openParams);
     182            0 :     if (!prj.ret) {
     183            0 :         return prj.ret;
     184              :     }
     185              : 
     186            0 :     INotationPtr notation = prj.val->masterNotation()->notation();
     187              : 
     188            0 :     Ret ret = ConverterUtils::applyTranspose(notation, optionsJson);
     189            0 :     if (!ret) {
     190            0 :         return ret;
     191              :     }
     192              : 
     193            0 :     QFile outputFile;
     194            0 :     openOutputFile(outputFile, out);
     195              : 
     196            0 :     BackendJsonWriter jsonWriter(&outputFile);
     197              : 
     198            0 :     bool result = doExportScoreTranspose(iocCtx, notation, jsonWriter);
     199              : 
     200            0 :     return result ? make_ret(Ret::Code::Ok) : make_ret(Ret::Code::InternalError);
     201            0 : }
     202              : 
     203            0 : Ret BackendApi::exportScoreElements(const muse::modularity::ContextPtr& iocCtx, const muse::io::path_t& in, const muse::io::path_t& out,
     204              :                                     const OpenParams& openParams)
     205              : {
     206            0 :     TRACEFUNC;
     207              : 
     208            0 :     RetVal<INotationProjectPtr> prj = openProject(iocCtx, in, openParams);
     209            0 :     if (!prj.ret) {
     210            0 :         return prj.ret;
     211              :     }
     212              : 
     213            0 :     INotationPtr notation = prj.val->masterNotation()->notation();
     214              : 
     215            0 :     QFile outputFile;
     216            0 :     openOutputFile(outputFile, out);
     217              : 
     218            0 :     return doExportScoreElements(notation, outputFile);
     219            0 : }
     220              : 
     221            0 : Ret BackendApi::openOutputFile(QFile& file, const muse::io::path_t& out)
     222              : {
     223            0 :     bool ok = false;
     224            0 :     if (!out.empty()) {
     225            0 :         file.setFileName(out.toQString());
     226            0 :         ok = file.open(QFile::WriteOnly);
     227              :     } else {
     228            0 :         ok = file.open(stdout, QFile::WriteOnly);
     229              :     }
     230              : 
     231            0 :     return ok ? make_ret(Ret::Code::Ok) : make_ret(Ret::Code::InternalError);
     232              : }
     233              : 
     234            0 : RetVal<project::INotationProjectPtr> BackendApi::openProject(const muse::modularity::ContextPtr& iocCtx, const muse::io::path_t& path,
     235              :                                                              const OpenParams& params)
     236              : {
     237            0 :     TRACEFUNC
     238              : 
     239            0 :     auto notationProject = notationCreator()->newProject(iocCtx);
     240            0 :     IF_ASSERT_FAILED(notationProject) {
     241            0 :         return make_ret(Ret::Code::InternalError);
     242              :     }
     243              : 
     244            0 :     Ret ret = notationProject->load(path, params);
     245            0 :     if (!ret) {
     246            0 :         LOGE() << "failed load: " << path << ", ret: " << ret.toString();
     247            0 :         return make_ret(Ret::Code::InternalError);
     248              :     }
     249              : 
     250            0 :     IMasterNotationPtr masterNotation = notationProject->masterNotation();
     251            0 :     if (!masterNotation) {
     252            0 :         return make_ret(Ret::Code::InternalError);
     253              :     }
     254              : 
     255            0 :     INotationPtr notation = masterNotation->notation();
     256            0 :     if (!notation) {
     257            0 :         return make_ret(Ret::Code::InternalError);
     258              :     }
     259              : 
     260            0 :     switchToPageView(masterNotation);
     261            0 :     renderExcerptsContents(masterNotation);
     262              : 
     263            0 :     return RetVal<INotationProjectPtr>::make_ok(notationProject);
     264            0 : }
     265              : 
     266            0 : QVariantMap BackendApi::readBeatsColors(const muse::io::path_t& filePath)
     267              : {
     268            0 :     TRACEFUNC
     269              : 
     270            0 :     if (filePath.empty()) {
     271            0 :         return QVariantMap();
     272              :     }
     273              : 
     274            0 :     RetVal<ByteArray> fileData = fileSystem()->readFile(filePath);
     275            0 :     if (!fileData.ret) {
     276            0 :         LOGW() << fileData.ret.toString();
     277            0 :         return QVariantMap();
     278              :     }
     279              : 
     280            0 :     const QJsonDocument document = QJsonDocument::fromJson(fileData.val.toQByteArrayNoCopy());
     281            0 :     const QJsonObject obj = document.object();
     282            0 :     const QJsonArray colors = obj.value("highlight").toArray();
     283              : 
     284            0 :     QVariantMap result;
     285              : 
     286            0 :     for (const auto colorObj : colors) {
     287            0 :         const QJsonArray beatsIndexes = colorObj[u"beats"].toArray();
     288            0 :         const QColor beatsColor = QColor(colorObj[u"color"].toString());
     289              : 
     290            0 :         for (const auto index : beatsIndexes) {
     291            0 :             result[index.toString()] = beatsColor;
     292              :         }
     293            0 :     }
     294              : 
     295            0 :     return result;
     296            0 : }
     297              : 
     298            0 : Ret BackendApi::exportScorePngs(const INotationPtr notation, BackendJsonWriter& jsonWriter, bool addSeparator)
     299              : {
     300            0 :     TRACEFUNC
     301              : 
     302            0 :     auto pngWriter = writers()->writer(PNG_WRITER_NAME);
     303            0 :     if (!pngWriter) {
     304            0 :         LOGW() << "Not found writer " << PNG_WRITER_NAME;
     305            0 :         return make_ret(Ret::Code::InternalError);
     306              :     }
     307              : 
     308            0 :     jsonWriter.addKey("pngs");
     309            0 :     jsonWriter.openArray();
     310              : 
     311            0 :     const PageList& notationPages = notation->elements()->pages();
     312              : 
     313            0 :     bool result = true;
     314            0 :     for (size_t i = 0; i < notationPages.size(); ++i) {
     315            0 :         ByteArray pngData;
     316            0 :         auto pngDevice = Buffer::opened(IODevice::ReadWrite, &pngData);
     317              : 
     318              :         INotationWriter::Options options = {
     319            0 :             { INotationWriter::OptionKey::PAGE_NUMBER, Val(static_cast<int>(i)) },
     320            0 :             { INotationWriter::OptionKey::TRANSPARENT_BACKGROUND, Val(false) }
     321            0 :         };
     322              : 
     323            0 :         Ret writeRet = pngWriter->write(notation, pngDevice, options);
     324            0 :         if (!writeRet) {
     325            0 :             LOGW() << writeRet.toString();
     326            0 :             result = false;
     327              :         }
     328              : 
     329            0 :         bool lastArrayValue = ((notationPages.size() - 1) == i);
     330            0 :         jsonWriter.addValue(pngData.toQByteArrayNoCopy().toBase64(), !lastArrayValue);
     331            0 :     }
     332              : 
     333            0 :     jsonWriter.closeArray(addSeparator);
     334              : 
     335            0 :     return result ? make_ret(Ret::Code::Ok) : make_ret(Ret::Code::InternalError);
     336            0 : }
     337              : 
     338            0 : Ret BackendApi::exportScoreSvgs(const INotationPtr notation, const muse::io::path_t& highlightConfigPath, BackendJsonWriter& jsonWriter,
     339              :                                 bool addSeparator)
     340              : {
     341            0 :     TRACEFUNC
     342              : 
     343            0 :     auto svgWriter = writers()->writer(SVG_WRITER_NAME);
     344            0 :     if (!svgWriter) {
     345            0 :         LOGW() << "Not found writer " << SVG_WRITER_NAME;
     346            0 :         return make_ret(Ret::Code::InternalError);
     347              :     }
     348              : 
     349            0 :     jsonWriter.addKey("svgs");
     350            0 :     jsonWriter.openArray();
     351              : 
     352            0 :     const PageList& notationPages = notation->elements()->pages();
     353            0 :     QVariantMap beatsColors = readBeatsColors(highlightConfigPath);
     354              : 
     355            0 :     bool result = true;
     356            0 :     for (size_t i = 0; i < notationPages.size(); ++i) {
     357            0 :         ByteArray svgData;
     358            0 :         auto svgDevice = Buffer::opened(IODevice::ReadWrite, &svgData);
     359              : 
     360              :         INotationWriter::Options options {
     361            0 :             { INotationWriter::OptionKey::PAGE_NUMBER, Val(static_cast<int>(i)) },
     362            0 :             { INotationWriter::OptionKey::TRANSPARENT_BACKGROUND, Val(false) },
     363            0 :             { INotationWriter::OptionKey::BEATS_COLORS, Val::fromQVariant(beatsColors) }
     364            0 :         };
     365              : 
     366            0 :         Ret writeRet = svgWriter->write(notation, svgDevice, options);
     367            0 :         if (!writeRet) {
     368            0 :             LOGW() << writeRet.toString();
     369            0 :             result = false;
     370              :         }
     371              : 
     372            0 :         bool lastArrayValue = ((notationPages.size() - 1) == i);
     373            0 :         jsonWriter.addValue(svgData.toQByteArrayNoCopy().toBase64(), !lastArrayValue);
     374            0 :     }
     375              : 
     376            0 :     jsonWriter.closeArray(addSeparator);
     377              : 
     378            0 :     return result ? make_ret(Ret::Code::Ok) : make_ret(Ret::Code::InternalError);
     379            0 : }
     380              : 
     381            0 : Ret BackendApi::exportScoreElementsPositions(const std::string& elementsPositionsWriterName, const std::string& elementsPositionsTagName,
     382              :                                              const INotationPtr notation, BackendJsonWriter& jsonWriter, bool addSeparator)
     383              : {
     384            0 :     TRACEFUNC
     385              : 
     386            0 :     RetVal<QByteArray> writerRetVal = processWriter(elementsPositionsWriterName, notation);
     387            0 :     if (!writerRetVal.ret) {
     388            0 :         return writerRetVal.ret;
     389              :     }
     390              : 
     391            0 :     jsonWriter.addKey(elementsPositionsTagName.c_str());
     392            0 :     jsonWriter.addValue(writerRetVal.val, addSeparator, false);
     393              : 
     394            0 :     return make_ret(Ret::Code::Ok);
     395            0 : }
     396              : 
     397            0 : Ret BackendApi::exportScorePdf(const INotationPtr notation, BackendJsonWriter& jsonWriter, bool addSeparator)
     398              : {
     399            0 :     TRACEFUNC
     400              : 
     401            0 :     RetVal<QByteArray> writerRetVal = processWriter(PDF_WRITER_NAME, notation);
     402            0 :     if (!writerRetVal.ret) {
     403            0 :         return writerRetVal.ret;
     404              :     }
     405              : 
     406            0 :     jsonWriter.addKey(PDF_WRITER_NAME.c_str());
     407            0 :     jsonWriter.addValue(writerRetVal.val, addSeparator);
     408              : 
     409            0 :     return make_ret(Ret::Code::Ok);
     410            0 : }
     411              : 
     412            0 : Ret BackendApi::exportScorePdf(const INotationPtr notation, QIODevice& destinationDevice)
     413              : {
     414            0 :     TRACEFUNC
     415              : 
     416            0 :     RetVal<QByteArray> writerRetVal = processWriter(PDF_WRITER_NAME, notation);
     417            0 :     if (!writerRetVal.ret) {
     418            0 :         return writerRetVal.ret;
     419              :     }
     420              : 
     421            0 :     bool ok = destinationDevice.write(QJsonDocument::fromJson(writerRetVal.val).toJson(QJsonDocument::Compact)) != -1;
     422            0 :     destinationDevice.close();
     423              : 
     424            0 :     return ok ? make_ret(Ret::Code::Ok) : make_ret(Ret::Code::InternalError);
     425            0 : }
     426              : 
     427            0 : Ret BackendApi::exportScoreMidi(const INotationPtr notation, BackendJsonWriter& jsonWriter, bool addSeparator)
     428              : {
     429            0 :     TRACEFUNC
     430              : 
     431            0 :     RetVal<QByteArray> writerRetVal = processWriter(MIDI_WRITER_NAME, notation);
     432            0 :     if (!writerRetVal.ret) {
     433            0 :         return writerRetVal.ret;
     434              :     }
     435              : 
     436            0 :     jsonWriter.addKey(MIDI_WRITER_NAME.c_str());
     437            0 :     jsonWriter.addValue(writerRetVal.val, addSeparator);
     438              : 
     439            0 :     return make_ret(Ret::Code::Ok);
     440            0 : }
     441              : 
     442            0 : Ret BackendApi::exportScoreMusicXML(const INotationPtr notation, BackendJsonWriter& jsonWriter, bool addSeparator)
     443              : {
     444            0 :     TRACEFUNC
     445              : 
     446            0 :     RetVal<QByteArray> writerRetVal = processWriter(MUSICXML_WRITER_NAME, notation);
     447            0 :     if (!writerRetVal.ret) {
     448            0 :         return writerRetVal.ret;
     449              :     }
     450              : 
     451            0 :     jsonWriter.addKey(MUSICXML_JSON_NAME.c_str());
     452            0 :     jsonWriter.addValue(writerRetVal.val, addSeparator);
     453              : 
     454            0 :     return make_ret(Ret::Code::Ok);
     455            0 : }
     456              : 
     457            0 : Ret BackendApi::exportScoreMetaData(const INotationPtr notation, BackendJsonWriter& jsonWriter, bool addSeparator)
     458              : {
     459            0 :     TRACEFUNC
     460              : 
     461            0 :     RetVal<std::string> meta = NotationMeta::metaJson(notation);
     462            0 :     if (!meta.ret) {
     463            0 :         LOGW() << meta.ret.toString();
     464            0 :         return meta.ret;
     465              :     }
     466              : 
     467            0 :     jsonWriter.addKey(META_DATA_NAME.c_str());
     468            0 :     jsonWriter.addValue(QString::fromStdString(meta.val).toUtf8(), addSeparator, true);
     469              : 
     470            0 :     return make_ret(Ret::Code::Ok);
     471            0 : }
     472              : 
     473            0 : Ret BackendApi::devInfo(const notation::INotationPtr notation, BackendJsonWriter& jsonWriter, bool addSeparator)
     474              : {
     475              :     UNUSED(notation);
     476              : 
     477            0 :     TRACEFUNC
     478              : 
     479            0 :     QJsonObject infoObj;
     480            0 :     infoObj["version"] = String("%1(%2)").arg(application()->fullVersion().toString(),
     481            0 :                                               application()->revision()).toQString();
     482              : 
     483            0 :     jsonWriter.addKey(DEV_INFO_NAME.c_str());
     484            0 :     jsonWriter.addValue(QJsonDocument(infoObj).toJson(), addSeparator, true);
     485              : 
     486            0 :     return make_ret(Ret::Code::Ok);
     487            0 : }
     488              : 
     489            0 : RetVal<QByteArray> BackendApi::processWriter(const std::string& writerName, const INotationPtr notation)
     490              : {
     491            0 :     auto writer = writers()->writer(writerName);
     492            0 :     if (!writer) {
     493            0 :         LOGW() << "Not found writer " << writerName;
     494            0 :         return make_ret(Ret::Code::InternalError);
     495              :     }
     496              : 
     497            0 :     ByteArray data;
     498            0 :     auto device = Buffer::opened(IODevice::ReadWrite, &data);
     499              : 
     500            0 :     Ret writeRet = writer->write(notation, device);
     501            0 :     if (!writeRet) {
     502            0 :         LOGW() << writeRet.toString();
     503            0 :         return writeRet;
     504              :     }
     505              : 
     506            0 :     RetVal<QByteArray> result;
     507            0 :     result.ret = make_ret(Ret::Code::Ok);
     508            0 :     result.val = data.toQByteArrayNoCopy().toBase64();
     509              : 
     510            0 :     device.close();
     511              : 
     512            0 :     return result;
     513            0 : }
     514              : 
     515            0 : RetVal<QByteArray> BackendApi::processWriter(const std::string& writerName, const INotationPtrList notations,
     516              :                                              const INotationWriter::Options& options)
     517              : {
     518            0 :     auto writer = writers()->writer(writerName);
     519            0 :     if (!writer) {
     520            0 :         LOGW() << "Not found writer " << writerName;
     521            0 :         return make_ret(Ret::Code::InternalError);
     522              :     }
     523              : 
     524            0 :     ByteArray data;
     525            0 :     auto device = Buffer::opened(IODevice::ReadWrite, &data);
     526              : 
     527            0 :     Ret writeRet = writer->writeList(notations, device, options);
     528            0 :     if (!writeRet) {
     529            0 :         LOGW() << writeRet.toString();
     530            0 :         return writeRet;
     531              :     }
     532              : 
     533            0 :     RetVal<QByteArray> result;
     534            0 :     result.ret = make_ret(Ret::Code::Ok);
     535            0 :     result.val = data.toQByteArrayNoCopy().toBase64();
     536              : 
     537            0 :     device.close();
     538              : 
     539            0 :     return result;
     540            0 : }
     541              : 
     542            0 : Ret BackendApi::doExportScoreParts(const muse::modularity::ContextPtr& iocCtx, const IMasterNotationPtr masterNotation,
     543              :                                    QIODevice& destinationDevice)
     544              : {
     545            0 :     QJsonArray partsObjList;
     546            0 :     QJsonArray partsMetaList;
     547            0 :     QJsonArray partsTitles;
     548              : 
     549            0 :     ExcerptNotationList excerpts = allExcerpts(masterNotation);
     550              : 
     551            0 :     for (const IExcerptNotationPtr& excerpt : excerpts) {
     552            0 :         mu::engraving::Score* partScore = excerpt->notation()->elements()->msScore();
     553            0 :         std::map<String, String> partMetaTags = partScore->metaTags();
     554              : 
     555            0 :         QJsonValue partTitle(partScore->name());
     556            0 :         partsTitles << partTitle;
     557              : 
     558            0 :         QVariantMap meta;
     559            0 :         for (const auto& [key, val] : partMetaTags) {
     560            0 :             meta[key] = val.toQString();
     561              :         }
     562              : 
     563            0 :         meta["open"] = partScore->isOpen();
     564              : 
     565            0 :         if (!partScore->eid().isValid()) {
     566            0 :             partScore->assignNewEID();
     567              :         }
     568            0 :         meta["id"] = QString::fromStdString(partScore->eid().toStdString());
     569              : 
     570            0 :         QJsonValue partMetaObj = QJsonObject::fromVariantMap(meta);
     571            0 :         partsMetaList << partMetaObj;
     572              : 
     573            0 :         std::string fileName = io::escapeFileName(partScore->name().toStdString()).toStdString() + ".mscz";
     574            0 :         QJsonValue partObj(QString::fromLatin1(scorePartJson(iocCtx, partScore, fileName).val));
     575            0 :         partsObjList << partObj;
     576            0 :     }
     577              : 
     578            0 :     QJsonObject json;
     579            0 :     json["parts"] = partsTitles;
     580            0 :     json["partsMeta"] = partsMetaList;
     581            0 :     json["partsBin"] = partsObjList;
     582              : 
     583            0 :     bool ok = destinationDevice.write(QJsonDocument(json).toJson(QJsonDocument::Compact)) != -1;
     584            0 :     destinationDevice.close();
     585              : 
     586            0 :     return ok ? make_ret(Ret::Code::Ok) : make_ret(Ret::Code::InternalError);
     587            0 : }
     588              : 
     589            0 : Ret BackendApi::doExportScorePartsPdfs(const IMasterNotationPtr masterNotation, QIODevice& destinationDevice,
     590              :                                        const std::string& scoreFileName)
     591              : {
     592            0 :     QJsonObject jsonForPdfs;
     593            0 :     jsonForPdfs["score"] = QString::fromStdString(scoreFileName);
     594            0 :     QByteArray scoreBin = processWriter(PDF_WRITER_NAME, masterNotation->notation()).val;
     595            0 :     jsonForPdfs["scoreBin"] = QLatin1String(scoreBin);
     596              : 
     597            0 :     INotationPtrList notations;
     598            0 :     notations.push_back(masterNotation->notation());
     599              : 
     600            0 :     QJsonArray partsArray;
     601            0 :     QJsonArray partsNamesArray;
     602              : 
     603            0 :     ExcerptNotationList excerpts = allExcerpts(masterNotation);
     604              : 
     605            0 :     for (const IExcerptNotationPtr& e : excerpts) {
     606            0 :         QJsonValue partNameVal(e->name());
     607            0 :         partsNamesArray.append(partNameVal);
     608              : 
     609            0 :         QByteArray partBin = processWriter(PDF_WRITER_NAME, e->notation()).val;
     610            0 :         partsArray.append(QLatin1String(partBin));
     611              : 
     612            0 :         notations.push_back(e->notation());
     613            0 :     }
     614              : 
     615            0 :     jsonForPdfs["parts"] = partsNamesArray;
     616            0 :     jsonForPdfs["partsBin"] = partsArray;
     617              : 
     618            0 :     jsonForPdfs["scoreFullPostfix"] = QString("-Score_and_parts") + ".pdf";
     619              : 
     620              :     INotationWriter::Options options {
     621            0 :         { INotationWriter::OptionKey::UNIT_TYPE, Val(INotationWriter::UnitType::MULTI_PART) }
     622            0 :     };
     623              : 
     624            0 :     QByteArray fullScoreData = processWriter(PDF_WRITER_NAME, notations, options).val;
     625            0 :     jsonForPdfs["scoreFullBin"] = QLatin1String(fullScoreData);
     626              : 
     627            0 :     QJsonDocument jsonDoc(jsonForPdfs);
     628            0 :     bool ok = destinationDevice.write(QJsonDocument(jsonDoc).toJson(QJsonDocument::Compact)) != -1;
     629              : 
     630            0 :     return ok;
     631            0 : }
     632              : 
     633            0 : Ret BackendApi::doExportScoreTranspose(const muse::modularity::ContextPtr& iocCtx, const INotationPtr notation,
     634              :                                        BackendJsonWriter& jsonWriter, bool addSeparator)
     635              : {
     636            0 :     mu::engraving::Score* score = notation->elements()->msScore();
     637              : 
     638            0 :     jsonWriter.addKey("mscz");
     639              : 
     640            0 :     std::string fileNumber = std::to_string(QRandomGenerator::global()->generate() % 1000000);
     641            0 :     std::string fileName = score->name().toStdString() + "_transposed." + fileNumber + ".mscz";
     642              : 
     643            0 :     RetVal<QByteArray> scoreJson = scorePartJson(iocCtx, score, fileName);
     644            0 :     if (!scoreJson.ret) {
     645            0 :         LOGW() << "Transpose: adding mscz failed";
     646              :     }
     647              : 
     648            0 :     jsonWriter.addValue(scoreJson.val, ADD_SEPARATOR);
     649              : 
     650            0 :     Ret ret = exportScorePdf(notation, jsonWriter, addSeparator);
     651            0 :     return ret;
     652            0 : }
     653              : 
     654            0 : muse::Ret BackendApi::doExportScoreElements(const notation::INotationPtr notation, QIODevice& out)
     655              : {
     656            0 :     mu::engraving::Score* score = notation->elements()->msScore();
     657            0 :     ElementMap elements = ScoreElementScanner::scanElements(score);
     658              : 
     659            0 :     QJsonArray rootArray;
     660              : 
     661            0 :     auto writeLocation = [](const ElementInfo::Location& loc, QJsonObject& obj) {
     662            0 :         if (loc.staffIdx != muse::nidx) {
     663            0 :             obj["staffIdx"] = static_cast<int>(loc.staffIdx);
     664              :         }
     665              : 
     666            0 :         if (loc.voiceIdx != muse::nidx) {
     667            0 :             obj["voiceIdx"] = static_cast<int>(loc.voiceIdx);
     668              :         }
     669              : 
     670            0 :         if (loc.measureIdx != muse::nidx) {
     671            0 :             obj["measureIdx"] = static_cast<int>(loc.measureIdx);
     672              :         }
     673              : 
     674            0 :         if (RealIsEqualOrMore(loc.beat, 0.f)) {
     675            0 :             obj["beat"] = loc.beat;
     676              :         }
     677            0 :     };
     678              : 
     679            0 :     auto writeDuration = [](const ElementInfo::Duration& dur, QJsonObject& obj) {
     680            0 :         QJsonObject durObj;
     681            0 :         durObj["name"] = dur.name.toQString();
     682            0 :         if (dur.dots > 0) {
     683            0 :             durObj["dots"] = dur.dots;
     684              :         }
     685            0 :         obj["duration"] = durObj;
     686            0 :     };
     687              : 
     688            0 :     auto writeNotes = [](const ElementInfo::NoteList& notes, QJsonObject& obj) {
     689            0 :         QJsonArray noteArray;
     690              : 
     691            0 :         for (const ElementInfo::Note& note : notes) {
     692            0 :             QJsonObject noteObj;
     693            0 :             noteObj["name"] = note.name.toQString();
     694            0 :             for (const auto& [key, val] : note.data) {
     695            0 :                 noteObj[key.toQString()] = val.toQString();
     696              :             }
     697            0 :             noteArray << noteObj;
     698            0 :         }
     699              : 
     700            0 :         obj["notes"] = noteArray;
     701            0 :     };
     702              : 
     703            0 :     for (const auto& instrumentPair : elements) {
     704            0 :         QJsonArray elementArray;
     705              : 
     706            0 :         for (const ElementInfo& element : instrumentPair.second) {
     707            0 :             QJsonObject obj;
     708            0 :             obj["type"] = mu::engraving::TConv::toXml(element.type).ascii();
     709              : 
     710            0 :             if (!element.name.empty()) {
     711            0 :                 obj["name"] = element.name.toQString();
     712              :             }
     713              : 
     714            0 :             if (!element.notes.empty()) {
     715            0 :                 writeNotes(element.notes, obj);
     716              :             }
     717              : 
     718            0 :             if (!element.duration.name.empty()) {
     719            0 :                 writeDuration(element.duration, obj);
     720              :             }
     721              : 
     722            0 :             for (const auto& [key, val] : element.data) {
     723            0 :                 obj[key.toQString()] = val.toQString();
     724              :             }
     725              : 
     726            0 :             if (element.start == element.end) {
     727            0 :                 writeLocation(element.start, obj);
     728              :             } else {
     729            0 :                 QJsonObject start, end;
     730            0 :                 writeLocation(element.start, start);
     731            0 :                 writeLocation(element.end, end);
     732            0 :                 obj["start"] = start;
     733            0 :                 obj["end"] = end;
     734            0 :             }
     735              : 
     736            0 :             elementArray << obj;
     737            0 :         }
     738              : 
     739            0 :         QJsonObject instrumentObj;
     740            0 :         instrumentObj["instrumentId"] = instrumentPair.first.instrumentId.toQString();
     741            0 :         instrumentObj["partId"] = instrumentPair.first.partId.toQString();
     742            0 :         instrumentObj["elements"] = elementArray;
     743            0 :         rootArray << instrumentObj;
     744            0 :     }
     745              : 
     746            0 :     out.write(QJsonDocument(rootArray).toJson(QJsonDocument::Compact));
     747              : 
     748            0 :     return make_ok();
     749            0 : }
     750              : 
     751            0 : RetVal<QByteArray> BackendApi::scorePartJson(const muse::modularity::ContextPtr& iocCtx, mu::engraving::Score* score,
     752              :                                              const std::string& fileName)
     753              : {
     754            0 :     ByteArray scoreData;
     755            0 :     Buffer buf(&scoreData);
     756              : 
     757            0 :     MscWriter::Params params;
     758            0 :     params.device = &buf;
     759            0 :     params.filePath = QString::fromStdString(fileName);
     760            0 :     params.mode = MscIoMode::Zip;
     761              : 
     762            0 :     MscWriter mscWriter(params);
     763            0 :     mscWriter.open();
     764              : 
     765            0 :     bool ok = MscSaver(iocCtx).exportPart(score, mscWriter);
     766            0 :     if (!ok) {
     767            0 :         LOGW() << "Error save mscz file";
     768              :     }
     769              : 
     770            0 :     mscWriter.close();
     771            0 :     if (mscWriter.hasError()) {
     772            0 :         ok = false;
     773            0 :         LOGW() << "Error write mscz file";
     774              :     }
     775              : 
     776            0 :     QByteArray ba = QByteArray::fromRawData(reinterpret_cast<const char*>(scoreData.constData()), static_cast<int>(scoreData.size()));
     777              : 
     778            0 :     RetVal<QByteArray> result;
     779            0 :     result.ret = ok ? make_ret(Ret::Code::Ok) : make_ret(Ret::Code::InternalError);
     780            0 :     result.val = ba.toBase64();
     781              : 
     782            0 :     return result;
     783            0 : }
     784              : 
     785            0 : void BackendApi::switchToPageView(IMasterNotationPtr masterNotation)
     786              : {
     787              :     //! NOTE: All operations must be done in page view mode
     788            0 :     masterNotation->notation()->setViewMode(ViewMode::PAGE);
     789            0 :     for (const IExcerptNotationPtr& excerpt : masterNotation->excerpts()) {
     790            0 :         excerpt->notation()->setViewMode(ViewMode::PAGE);
     791              :     }
     792            0 : }
     793              : 
     794            0 : void BackendApi::renderExcerptsContents(IMasterNotationPtr masterNotation)
     795              : {
     796              :     //! NOTE: Due to optimization, only the master score is layouted
     797              :     //!       Let's layout all the scores of the excerpts
     798            0 :     for (const IExcerptNotationPtr& excerpt : masterNotation->excerpts()) {
     799            0 :         Score* score = excerpt->notation()->elements()->msScore();
     800            0 :         if (!score->autoLayoutEnabled()) {
     801            0 :             score->doLayout();
     802              :         }
     803              :     }
     804            0 : }
     805              : 
     806            0 : ExcerptNotationList BackendApi::allExcerpts(notation::IMasterNotationPtr masterNotation)
     807              : {
     808            0 :     initPotentialExcerpts(masterNotation);
     809              : 
     810            0 :     ExcerptNotationList excerpts = masterNotation->excerpts();
     811            0 :     ExcerptNotationList potentialExcerpts = masterNotation->potentialExcerpts();
     812            0 :     excerpts.insert(excerpts.end(), potentialExcerpts.begin(), potentialExcerpts.end());
     813              : 
     814            0 :     masterNotation->sortExcerpts(excerpts);
     815              : 
     816            0 :     return excerpts;
     817            0 : }
     818              : 
     819            0 : void BackendApi::initPotentialExcerpts(notation::IMasterNotationPtr masterNotation)
     820              : {
     821            0 :     ExcerptNotationList potentialExcerpts = masterNotation->potentialExcerpts();
     822              : 
     823            0 :     masterNotation->initExcerpts(potentialExcerpts);
     824            0 :     renderExcerptsContents(masterNotation);
     825            0 : }
     826              : 
     827            0 : Ret BackendApi::updateSource(const muse::modularity::ContextPtr& iocCtx, const muse::io::path_t& in, const std::string& newSource,
     828              :                              bool forceMode)
     829              : {
     830            0 :     OpenParams openParams;
     831            0 :     openParams.forceMode = forceMode;
     832              : 
     833            0 :     RetVal<INotationProjectPtr> project = openProject(iocCtx, in, openParams);
     834            0 :     if (!project.ret) {
     835            0 :         return project.ret;
     836              :     }
     837              : 
     838            0 :     ProjectMeta meta = project.val->metaInfo();
     839            0 :     meta.source = QString::fromStdString(newSource);
     840              : 
     841            0 :     project.val->setMetaInfo(meta);
     842              : 
     843            0 :     return project.val->save();
     844            0 : }
        

Generated by: LCOV version 2.0-1