tdf#159483 sc HTML copy: handle data-sheets-value attribute for the num case

Type 1000 and 2000 into cells in Calc, format them as e.g. a HUF
currency, the resulting formatted strings can be copied to google
sheets, but the SUM() on them will be 0 (and not 3000).

Our own import knows how to read metadata to get the original float
value, but our exporter didn't emit these.

Fix the problem by adding support for non-boolean float values + number
formats in the HTML export.

This is more or less export equivalent of commit
789964785a61daab5f8065f006dd7aaf843c7236 (tdf#159483 sc HTML import:
handle data-sheets-value attribute for the num case, 2024-02-09).

Change-Id: I5acb3724367ce38bd96056dbe4f846cd7bbc5fe2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164213
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <[email protected]>
diff --git a/sc/qa/filter/html/html.cxx b/sc/qa/filter/html/html.cxx
index fb9a436..916dd23 100644
--- a/sc/qa/filter/html/html.cxx
+++ b/sc/qa/filter/html/html.cxx
@@ -239,6 +239,43 @@ CPPUNIT_TEST_FIXTURE(Test, testCopyBoolean)
    assertXPath(pHtmlDoc, "(//td)[1]"_ostr, "data-sheets-value"_ostr, "{ \"1\": 4, \"4\": 1}");
    assertXPath(pHtmlDoc, "(//td)[2]"_ostr, "data-sheets-value"_ostr, "{ \"1\": 4, \"4\": 0}");
}

CPPUNIT_TEST_FIXTURE(Test, testCopyFormattedNumber)
{
    // Given a document with formatted numbers in A1-A2:
    createScDoc();
    ScDocument* pDoc = getScDoc();
    sal_Int32 nCheckPos;
    SvNumFormatType nType;
    sal_uInt32 nFormat;
    OUString aNumberFormat("#,##0.00");
    SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
    pFormatter->PutEntry(aNumberFormat, nCheckPos, nType, nFormat);
    ScAddress aCellPos1(/*nColP=*/0, /*nRowP=*/0, /*nTabP=*/0);
    pDoc->SetNumberFormat(aCellPos1, nFormat);
    pDoc->SetString(aCellPos1, "1000");
    ScAddress aCellPos2(/*nColP=*/0, /*nRowP=*/1, /*nTabP=*/0);
    pDoc->SetNumberFormat(aCellPos2, nFormat);
    pDoc->SetString(aCellPos2, "2000");

    // When copying those values:
    ScImportExport aExporter(*pDoc, ScRange(aCellPos1, aCellPos2));
    SvMemoryStream aStream;
    CPPUNIT_ASSERT(aExporter.ExportStream(aStream, OUString(), SotClipboardFormatId::HTML));

    // Then make sure the values are numbers:
    aStream.Seek(0);
    htmlDocUniquePtr pHtmlDoc = parseHtmlStream(&aStream);
    // Without the accompanying fix in place, this test would have failed with:
    // - XPath '(//td)[1]' no attribute 'data-sheets-value' exist
    // i.e. only a formatted number string was written, without a float value.
    assertXPath(pHtmlDoc, "(//td)[1]"_ostr, "data-sheets-value"_ostr, "{ \"1\": 3, \"3\": 1000}");
    assertXPath(pHtmlDoc, "(//td)[1]"_ostr, "data-sheets-numberformat"_ostr,
                "{ \"1\": 2, \"2\": \"#,##0.00\", \"3\": 1}");
    assertXPath(pHtmlDoc, "(//td)[2]"_ostr, "data-sheets-value"_ostr, "{ \"1\": 3, \"3\": 2000}");
    assertXPath(pHtmlDoc, "(//td)[2]"_ostr, "data-sheets-numberformat"_ostr,
                "{ \"1\": 2, \"2\": \"#,##0.00\", \"3\": 1}");
}
}

CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/source/filter/html/htmlexp.cxx b/sc/source/filter/html/htmlexp.cxx
index 1e5dcf2..c2554b7 100644
--- a/sc/source/filter/html/htmlexp.cxx
+++ b/sc/source/filter/html/htmlexp.cxx
@@ -1146,6 +1146,7 @@ void ScHTMLExport::WriteCell( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SC
        nFormat, *pFormatter, &aNonConvertibleChars));

    std::optional<tools::JsonWriter> oJson;
    const SvNumberformat* pNumberFormat = nullptr;
    if (bValueData)
    {
        if (nFormat)
@@ -1161,6 +1162,14 @@ void ScHTMLExport::WriteCell( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SC
                    oJson->put("1", static_cast<sal_Int32>(4));
                    oJson->put("4", static_cast<sal_Int32>(fVal));
                }
                else
                {
                    // 3 is number.
                    oJson.emplace();
                    oJson->put("1", static_cast<sal_Int32>(3));
                    oJson->put("3", static_cast<sal_Int32>(fVal));
                    pNumberFormat = pFormatEntry;
                }
            }
        }
    }
@@ -1179,6 +1188,19 @@ void ScHTMLExport::WriteCell( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SC
                      + HTMLOutFuncs::ConvertStringToHTML(aJsonString) + "\"");
    }

    if (pNumberFormat)
    {
        // 2 is a number format.
        oJson.emplace();
        oJson->put("1", static_cast<sal_Int32>(2));
        oJson->put("2", pNumberFormat->GetFormatstring());
        // The number format is for a number.
        oJson->put("3", static_cast<sal_Int32>(1));
        OUString aJsonString = OUString::fromUtf8(oJson->finishAndGetAsOString());
        aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_DSnum "=\""
                      + HTMLOutFuncs::ConvertStringToHTML(aJsonString) + "\"");
    }

    TAG_ON(aStrTD.makeStringAndClear());

    //write the note for this as the first thing in the tag