Files
core/XlsxSerializerCom/Writer/CSVWriter.cpp
2018-09-28 14:30:27 +03:00

339 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* (c) Copyright Ascensio System SIA 2010-2018
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
* EU, LV-1021.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include "CSVWriter.h"
#include "../../UnicodeConverter/UnicodeConverter.h"
#include "../../UnicodeConverter/UnicodeConverter_Encodings.h"
#include "../../DesktopEditor/common/StringBuilder.h"
#include "../../Common/DocxFormat/Source/XlsxFormat/Workbook/Workbook.h"
#include "../../Common/DocxFormat/Source/XlsxFormat/SharedStrings/SharedStrings.h"
#include "../../Common/DocxFormat/Source/XlsxFormat/Styles/Styles.h"
#include "../../Common/DocxFormat/Source/XlsxFormat/Worksheets/Worksheet.h"
namespace CSVWriter
{
static std::wstring g_sNewLineN = _T("\n");
static std::wstring g_sEndJson = _T("]");
static std::wstring g_sQuote = _T("\"");
static std::wstring g_sDoubleQuote = _T("\"\"");
static std::wstring g_sBkt = _T("[");
static std::wstring g_sBktComma = _T(",[");
void escapeJson(const std::wstring& sInput, NSStringUtils::CStringBuilder& oBuilder)
{
//http://stackoverflow.com/questions/7724448/simple-json-string-escape-for-c
for (size_t i = 0; i < sInput.length(); ++i)
{
WCHAR c = sInput[i];
switch (c)
{
case '"': oBuilder.WriteString(_T("\\\"")); break;
case '\\': oBuilder.WriteString(_T("\\\\")); break;
case '\b': oBuilder.WriteString(_T("\\b")); break;
case '\f': oBuilder.WriteString(_T("\\f")); break;
case '\n': oBuilder.WriteString(_T("\\n")); break;
case '\r': oBuilder.WriteString(_T("\\r")); break;
case '\t': oBuilder.WriteString(_T("\\t")); break;
default:
if ('\x00' <= c && c <= '\x1f')
{
oBuilder.WriteString(_T("\\u00"));
oBuilder.WriteHexByte((unsigned char)c);
}
else
{
oBuilder.AddCharSafe(c);
}
}
}
}
void WriteFile(NSFile::CFileBinary *pFile, WCHAR **pWriteBuffer, INT &nCurrentIndex, const std::wstring &sWriteString, UINT &nCodePage, bool bIsEnd = false)
{
if (NULL == pFile || NULL == pWriteBuffer)
return;
size_t nCountChars = sWriteString.length();
if (0 == nCountChars && !bIsEnd)
return;
const size_t c_nSize = 1048576; // 1024 * 1024
const size_t nSizeWchar = sizeof(WCHAR);
if (NULL == *pWriteBuffer)
{
*pWriteBuffer = new WCHAR[c_nSize];
memset(*pWriteBuffer, 0, nSizeWchar * c_nSize);
nCurrentIndex = 0;
}
if (nCountChars + nCurrentIndex > c_nSize || bIsEnd)
{
// Буффер заполнился, пишем
if (nCodePage == 48 && 2 == sizeof(wchar_t))//todo 48 временно CP_UTF16
{
pFile->WriteFile((BYTE*)*pWriteBuffer, sizeof (WCHAR) * nCurrentIndex);
}
else
{
const NSUnicodeConverter::EncodindId& oEncodindId = NSUnicodeConverter::Encodings[nCodePage];
NSUnicodeConverter::CUnicodeConverter oUnicodeConverter;
std::string sFileDataA = oUnicodeConverter.fromUnicode(*pWriteBuffer, nCurrentIndex, oEncodindId.Name);
pFile->WriteFile((BYTE*)sFileDataA.c_str(), (DWORD)sFileDataA.length());
}
memset(*pWriteBuffer, 0, nSizeWchar * c_nSize);
nCurrentIndex = 0;
}
if (!bIsEnd)
{
memcpy(*pWriteBuffer + nCurrentIndex, sWriteString.c_str(), nCountChars * nSizeWchar);
nCurrentIndex += (int)nCountChars;
}
}
void WriteFromXlsxToCsv(const std::wstring &sFileDst, OOX::Spreadsheet::CXlsx &oXlsx, UINT nCodePage, const std::wstring& sDelimiter, bool bJSON)
{
CCSVWriter oWriter(oXlsx, nCodePage, sDelimiter, bJSON);
oWriter.Start(sFileDst);
if ( oXlsx.m_pWorkbook )
{
LONG lActiveSheet = oXlsx.m_pWorkbook->GetActiveSheetIndex();
std::wstring sSheetRId = _T("Sheet1"); // Читаем не по rId, а по имени листа
// Get active sheet rId (для конвертации в CSV нужно использовать name, т.к. это наш бинарник из js-скриптов и еще нет rId
// А для json-а нужно пользовать rId, т.к. при открытии они используются
if ( oXlsx.m_pWorkbook->m_oSheets.IsInit() && !oXlsx.m_pWorkbook->m_oSheets->m_arrItems.empty())
{
OOX::Spreadsheet::CSheet* pSheet = NULL;
if (lActiveSheet >= 0 && lActiveSheet < (LONG)oXlsx.m_pWorkbook->m_oSheets->m_arrItems.size())
{
pSheet = oXlsx.m_pWorkbook-> m_oSheets->m_arrItems[lActiveSheet];
}
else
{
pSheet = oXlsx.m_pWorkbook->m_oSheets->m_arrItems.front();
}
sSheetRId = bJSON ? pSheet->m_oRid->GetValue() : pSheet->m_oName.get2();
}
std::map<std::wstring, OOX::Spreadsheet::CWorksheet*>::const_iterator pFind = oXlsx.m_mapWorksheets.find(sSheetRId);
if (pFind != oXlsx.m_mapWorksheets.end())
{
OOX::Spreadsheet::CWorksheet *pWorksheet = pFind->second;
if (NULL != pWorksheet && pWorksheet->m_oSheetData.IsInit())
{
oWriter.WriteSheetStart(pWorksheet);
for (size_t i = 0; i < pWorksheet->m_oSheetData->m_arrItems.size(); ++i)
{
OOX::Spreadsheet::CRow *pRow = pWorksheet->m_oSheetData->m_arrItems[i];
oWriter.WriteRowStart(pRow);
for (size_t j = 0; j < pRow->m_arrItems.size(); ++j)
{
oWriter.WriteCell(pRow->m_arrItems[j]);
}
oWriter.WriteRowEnd(pRow);
}
oWriter.WriteSheetEnd(pWorksheet);
}
}
}
oWriter.End();
}
CCSVWriter::CCSVWriter(OOX::Spreadsheet::CXlsx &m_oXlsx, UINT m_nCodePage, const std::wstring& m_sDelimiter, bool m_bJSON): m_oXlsx(m_oXlsx), m_nCodePage(m_nCodePage), m_sDelimiter(m_sDelimiter), m_bJSON(m_bJSON)
{
m_pWriteBuffer = NULL;
m_nCurrentIndex = 0;
m_sEscape = _T("\"\n");
m_sEscape += m_sDelimiter;
m_nRowCurrent = 1;
m_nColCurrent = 1;
m_bIsWriteCell = false;
bool m_bStartRow = true;
bool m_bStartCell = true;
}
CCSVWriter::~CCSVWriter()
{
Close();
}
void CCSVWriter::Start(const std::wstring &sFileDst)
{
m_oFile.CreateFileW(sFileDst);
// Нужно записать шапку
if (46 == m_nCodePage)//todo 46 временно CP_UTF8
{
BYTE arUTF8[3] = {0xEF, 0xBB, 0xBF};
m_oFile.WriteFile(arUTF8, 3);
}
else if (48 == m_nCodePage)//todo 48 временно CP_UTF16
{
BYTE arUTF16[2] = {0xFF, 0xFE};
m_oFile.WriteFile(arUTF16, 2);
}
else if (49 == m_nCodePage)//todo 49 временно CP_unicodeFFFE
{
BYTE arBigEndian[2] = {0xFE, 0xFF};
m_oFile.WriteFile(arBigEndian, 2);
}
}
void CCSVWriter::WriteSheetStart(OOX::Spreadsheet::CWorksheet* pWorksheet)
{
m_nRowCurrent = 1;
if (m_bJSON)
CSVWriter::WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, g_sBkt, m_nCodePage);
}
void CCSVWriter::WriteRowStart(OOX::Spreadsheet::CRow *pRow)
{
INT nRow = pRow->m_oR.IsInit() ? pRow->m_oR->GetValue() : m_bStartRow ? m_nRowCurrent : m_nRowCurrent + 1;
if (m_bJSON)
CSVWriter::WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, (m_bStartRow ? g_sBkt: g_sBktComma), m_nCodePage);
else
{
while (nRow > m_nRowCurrent)
{
// Write new line
++m_nRowCurrent;
WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, g_sNewLineN, m_nCodePage);
}
}
m_bStartRow = false;
m_bStartCell = true;
m_nColCurrent = 1;
m_bIsWriteCell = false;
}
void CCSVWriter::WriteCell(OOX::Spreadsheet::CCell *pCell)
{
INT nRowTmp = 0;
INT nCol = 0;
if (pCell->isInitRef())
{
pCell->getRowCol(nRowTmp, nCol);
nRowTmp++;
nCol++;
}
else
{
nCol = m_bStartCell ? m_nColCurrent : m_nColCurrent + 1;
}
while (nCol > m_nColCurrent)
{
if (m_bJSON && false == m_bIsWriteCell)
{
// Запишем пустые строки (для JSON-а)
WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, g_sDoubleQuote, m_nCodePage);
}
// Write delimiter
++m_nColCurrent;
WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, m_sDelimiter, m_nCodePage);
m_bIsWriteCell = false;
}
// Get cell value
std::wstring sCellValue = _T("");
if (pCell->m_oValue.IsInit())
{
if (pCell->m_oType.IsInit() && SimpleTypes::Spreadsheet::celltypeNumber != pCell->m_oType->GetValue())
{
if(SimpleTypes::Spreadsheet::celltypeStr == pCell->m_oType->GetValue())
{
sCellValue = pCell->m_oValue->ToString();
}
else
{
int nValue = _wtoi(pCell->m_oValue->ToString().c_str());
if (nValue >= 0 && nValue < (int)m_oXlsx.m_pSharedStrings->m_arrItems.size())
{
OOX::Spreadsheet::CSi *pSi = m_oXlsx.m_pSharedStrings->m_arrItems[nValue];
if(NULL != pSi)
{
sCellValue = pSi->ToString();
}
}
}
}
else
{
sCellValue = pCell->m_oValue->ToString();
}
}
// Escape cell value
if(m_bJSON)
{
NSStringUtils::CStringBuilder oBuilder;
oBuilder.WriteString(_T("\""));
escapeJson(sCellValue, oBuilder);
oBuilder.WriteString(_T("\""));
sCellValue = std::wstring(oBuilder.GetBuffer(), oBuilder.GetCurSize());
}
else if (std::wstring::npos != sCellValue.find_first_of(m_sEscape))
{
NSStringExt::Replace(sCellValue, g_sQuote, g_sDoubleQuote);
sCellValue = g_sQuote + sCellValue + g_sQuote;
}
// Write cell value
WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, sCellValue, m_nCodePage);
m_bIsWriteCell = true;
m_bStartCell = false;
}
void CCSVWriter::WriteRowEnd(OOX::Spreadsheet::CRow* pWorksheet)
{
if (m_bJSON)
WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, g_sEndJson, m_nCodePage);
}
void CCSVWriter::WriteSheetEnd(OOX::Spreadsheet::CWorksheet* pWorksheet)
{
if (m_bJSON)
{
WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, g_sEndJson, m_nCodePage);
WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, g_sEndJson, m_nCodePage, true);
}
}
void CCSVWriter::End()
{
// Теперь мы пишем как MS Excel (новую строку записываем в файл)
if (!m_bJSON)
{
WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, g_sNewLineN, m_nCodePage);
WriteFile(&m_oFile, &m_pWriteBuffer, m_nCurrentIndex, g_sNewLineN, m_nCodePage, true);
}
Close();
}
void CCSVWriter::Close()
{
RELEASEARRAYOBJECTS(m_pWriteBuffer);
m_oFile.CloseFile();
}
}