diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/filter/ascii | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/filter/ascii')
-rw-r--r-- | sw/source/filter/ascii/ascatr.cxx | 388 | ||||
-rw-r--r-- | sw/source/filter/ascii/parasc.cxx | 521 | ||||
-rw-r--r-- | sw/source/filter/ascii/wrtasc.cxx | 230 | ||||
-rw-r--r-- | sw/source/filter/ascii/wrtasc.hxx | 45 |
4 files changed, 1184 insertions, 0 deletions
diff --git a/sw/source/filter/ascii/ascatr.cxx b/sw/source/filter/ascii/ascatr.cxx new file mode 100644 index 0000000000..c4f4d12490 --- /dev/null +++ b/sw/source/filter/ascii/ascatr.cxx @@ -0,0 +1,388 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <hintids.hxx> +#include <tools/stream.hxx> +#include <comphelper/string.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <ndtxt.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <redline.hxx> +#include "wrtasc.hxx" +#include <txatbase.hxx> +#include <txtfld.hxx> +#include <fmtftn.hxx> +#include <fmtfld.hxx> +#include <fldbas.hxx> +#include <ftninfo.hxx> +#include <numrule.hxx> + +#include <algorithm> + +/* + * This file contains all output functions of the ASCII-Writer; + * For all nodes, attributes, formats and chars. + */ + +namespace { + +class SwASC_AttrIter +{ + SwASCWriter& m_rWrt; + const SwTextNode& m_rNd; + sal_Int32 m_nCurrentSwPos; + + sal_Int32 SearchNext( sal_Int32 nStartPos ); + +public: + SwASC_AttrIter( SwASCWriter& rWrt, const SwTextNode& rNd, sal_Int32 nStt ); + + void NextPos() { m_nCurrentSwPos = SearchNext(m_nCurrentSwPos + 1); } + + sal_Int32 WhereNext() const { return m_nCurrentSwPos; } + + bool OutAttr( sal_Int32 nSwPos ); +}; + +} + +SwASC_AttrIter::SwASC_AttrIter(SwASCWriter& rWr, const SwTextNode& rTextNd, sal_Int32 nStt) + : m_rWrt(rWr) + , m_rNd(rTextNd) + , m_nCurrentSwPos(0) +{ + m_nCurrentSwPos = SearchNext(nStt + 1); +} + +sal_Int32 SwASC_AttrIter::SearchNext( sal_Int32 nStartPos ) +{ + sal_Int32 nMinPos = SAL_MAX_INT32; + const SwpHints* pTextAttrs = m_rNd.GetpSwpHints(); + if( pTextAttrs ) + { + // TODO: This can be optimized, if we make use of the fact that the TextAttrs + // are sorted by starting position. We would need to remember two indices, however. + for ( size_t i = 0; i < pTextAttrs->Count(); ++i ) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + if ( pHt->HasDummyChar() ) + { + sal_Int32 nPos = pHt->GetStart(); + + if( nPos >= nStartPos && nPos <= nMinPos ) + nMinPos = nPos; + + if( ( ++nPos ) >= nStartPos && nPos < nMinPos ) + nMinPos = nPos; + } + else if ( pHt->HasContent() ) + { + const sal_Int32 nHintStart = pHt->GetStart(); + if ( nHintStart >= nStartPos && nHintStart <= nMinPos ) + { + nMinPos = nHintStart; + } + + const sal_Int32 nHintEnd = pHt->End() ? *pHt->End() : COMPLETE_STRING; + if ( nHintEnd >= nStartPos && nHintEnd < nMinPos ) + { + nMinPos = nHintEnd; + } + } + } + } + return nMinPos; +} + +bool SwASC_AttrIter::OutAttr( sal_Int32 nSwPos ) +{ + bool bRet = false; + const SwpHints* pTextAttrs = m_rNd.GetpSwpHints(); + if( pTextAttrs ) + { + for( size_t i = 0; i < pTextAttrs->Count(); ++i ) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + if ( ( pHt->HasDummyChar() + || pHt->HasContent() ) + && nSwPos == pHt->GetStart() ) + { + bRet = true; + OUString sOut; + switch( pHt->Which() ) + { + case RES_TXTATR_FIELD: + case RES_TXTATR_ANNOTATION: + case RES_TXTATR_INPUTFIELD: + sOut = static_txtattr_cast<SwTextField const*>(pHt) + ->GetFormatField().GetField()->ExpandField(true, nullptr); + break; + + case RES_TXTATR_FTN: + { + const SwFormatFootnote& rFootnote = pHt->GetFootnote(); + if( !rFootnote.GetNumStr().isEmpty() ) + sOut = rFootnote.GetNumStr(); + else if( rFootnote.IsEndNote() ) + sOut = m_rWrt.m_pDoc->GetEndNoteInfo().m_aFormat.GetNumStr( + rFootnote.GetNumber()); + else + sOut = m_rWrt.m_pDoc->GetFootnoteInfo().m_aFormat.GetNumStr( + rFootnote.GetNumber()); + } + break; + case RES_TXTATR_LINEBREAK: + { + // Downgrade the clearing break to a simple linebreak. + sOut = OUStringChar(GetCharOfTextAttr(*pHt)); + break; + } + } + if( !sOut.isEmpty() ) + m_rWrt.Strm().WriteUnicodeOrByteText(sOut); + } + else if( nSwPos < pHt->GetStart() ) + break; + } + } + return bRet; +} + +namespace { + +class SwASC_RedlineIter +{ +private: + SwTextNode const& m_rNode; + IDocumentRedlineAccess const& m_rIDRA; + SwRedlineTable::size_type m_nextRedline; + +public: + SwASC_RedlineIter(SwASCWriter const& rWriter, SwTextNode const& rNode) + : m_rNode(rNode) + , m_rIDRA(rNode.GetDoc().getIDocumentRedlineAccess()) + , m_nextRedline(rWriter.m_bHideDeleteRedlines + ? m_rIDRA.GetRedlinePos(m_rNode, RedlineType::Delete) + : SwRedlineTable::npos) + { + } + + bool CheckNodeDeleted() + { + if (m_nextRedline == SwRedlineTable::npos) + { + return false; + } + SwRangeRedline const*const pRedline(m_rIDRA.GetRedlineTable()[m_nextRedline]); + return pRedline->Start()->GetNodeIndex() < m_rNode.GetIndex() + && m_rNode.GetIndex() < pRedline->End()->GetNodeIndex(); + } + + std::pair<sal_Int32, sal_Int32> GetNextRedlineSkip() + { + sal_Int32 nRedlineStart(COMPLETE_STRING); + sal_Int32 nRedlineEnd(COMPLETE_STRING); + for ( ; m_nextRedline < m_rIDRA.GetRedlineTable().size(); ++m_nextRedline) + { + SwRangeRedline const*const pRedline(m_rIDRA.GetRedlineTable()[m_nextRedline]); + if (pRedline->GetType() != RedlineType::Delete) + { + continue; + } + auto [pStart, pEnd] = pRedline->StartEnd(); // SwPosition* + if (m_rNode.GetIndex() < pStart->GetNodeIndex()) + { + m_nextRedline = SwRedlineTable::npos; + break; // done + } + if (nRedlineStart == COMPLETE_STRING) + { + nRedlineStart = pStart->GetNodeIndex() == m_rNode.GetIndex() + ? pStart->GetContentIndex() + : 0; + } + else + { + if (pStart->GetContentIndex() != nRedlineEnd) + { + assert(nRedlineEnd < pStart->GetContentIndex()); + break; // no increment, revisit it next call + } + } + nRedlineEnd = pEnd->GetNodeIndex() == m_rNode.GetIndex() + ? pEnd->GetContentIndex() + : COMPLETE_STRING; + } + return std::make_pair(nRedlineStart, nRedlineEnd); + } +}; + +} + +// Output of the node + +static Writer& OutASC_SwTextNode( Writer& rWrt, SwContentNode& rNode ) +{ + const SwTextNode& rNd = static_cast<SwTextNode&>(rNode); + + sal_Int32 nStrPos = rWrt.m_pCurrentPam->GetPoint()->GetContentIndex(); + const sal_Int32 nNodeEnd = rNd.Len(); + sal_Int32 nEnd = nNodeEnd; + bool bLastNd = rWrt.m_pCurrentPam->GetPoint()->GetNode() == rWrt.m_pCurrentPam->GetMark()->GetNode(); + if( bLastNd ) + nEnd = rWrt.m_pCurrentPam->GetMark()->GetContentIndex(); + + bool bIsOneParagraph = rWrt.m_pOrigPam->Start()->GetNode() == rWrt.m_pOrigPam->End()->GetNode() && !getenv("SW_ASCII_COPY_NUMBERING"); + + SwASC_AttrIter aAttrIter( static_cast<SwASCWriter&>(rWrt), rNd, nStrPos ); + SwASC_RedlineIter redlineIter(static_cast<SwASCWriter&>(rWrt), rNd); + + if (redlineIter.CheckNodeDeleted()) + { + return rWrt; + } + + const SwNumRule* pNumRule = rNd.GetNumRule(); + if (pNumRule && !nStrPos && rWrt.m_bExportParagraphNumbering && !bIsOneParagraph) + { + bool bIsOutlineNumRule = pNumRule == rNd.GetDoc().GetOutlineNumRule(); + + // indent each numbering level by 4 spaces + OUString level; + if (!bIsOutlineNumRule) + { + for (int i = 0; i <= rNd.GetActualListLevel(); ++i) + level += " "; + } + + // set up bullets or numbering + OUString numString(rNd.GetNumString()); + if (numString.isEmpty() && !bIsOutlineNumRule) + { + if (rNd.HasBullet() && !rNd.HasVisibleNumberingOrBullet()) + numString = " "; + else if (rNd.HasBullet()) + numString = OUString(numfunc::GetBulletChar(rNd.GetActualListLevel())); + else if (!rNd.HasBullet() && !rNd.HasVisibleNumberingOrBullet()) + numString = " "; + } + + if (!level.isEmpty() || !numString.isEmpty()) + rWrt.Strm().WriteUnicodeOrByteText(Concat2View(level + numString + " ")); + } + + OUString aStr( rNd.GetText() ); + if( rWrt.m_bASCII_ParaAsBlank ) + aStr = aStr.replace(0x0A, ' '); + + const bool bExportSoftHyphens = RTL_TEXTENCODING_UCS2 == rWrt.GetAsciiOptions().GetCharSet() || + RTL_TEXTENCODING_UTF8 == rWrt.GetAsciiOptions().GetCharSet(); + + std::pair<sal_Int32, sal_Int32> curRedline(redlineIter.GetNextRedlineSkip()); + for (;;) { + const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd); + + bool isOutAttr(false); + if (nStrPos < curRedline.first || curRedline.second <= nStrPos) + { + isOutAttr = aAttrIter.OutAttr(nStrPos); + } + + if (!isOutAttr) + { + OUStringBuffer buf; + while (true) + { + if (nNextAttr <= curRedline.first) + { + buf.append(aStr.subView(nStrPos, nNextAttr - nStrPos)); + break; + } + else if (nStrPos < curRedline.second) + { + if (nStrPos < curRedline.first) + { + buf.append(aStr.subView(nStrPos, curRedline.first - nStrPos)); + } + if (curRedline.second <= nNextAttr) + { + nStrPos = curRedline.second; + curRedline = redlineIter.GetNextRedlineSkip(); + } + else + { + nStrPos = nNextAttr; + break; + } + } + else + { + curRedline = redlineIter.GetNextRedlineSkip(); + } + } + OUString aOutStr(buf.makeStringAndClear()); + if ( !bExportSoftHyphens ) + aOutStr = aOutStr.replaceAll(OUStringChar(CHAR_SOFTHYPHEN), ""); + + // all INWORD should be already removed by OutAttr + // but the field-marks are not attributes so filter those + static sal_Unicode const forbidden [] = { + CH_TXT_ATR_INPUTFIELDSTART, + CH_TXT_ATR_INPUTFIELDEND, + CH_TXT_ATR_FORMELEMENT, + CH_TXT_ATR_FIELDSTART, + CH_TXT_ATR_FIELDSEP, + CH_TXT_ATR_FIELDEND, + CH_TXTATR_BREAKWORD, + 0 + }; + aOutStr = comphelper::string::removeAny(aOutStr, forbidden); + + rWrt.Strm().WriteUnicodeOrByteText( aOutStr ); + } + nStrPos = nNextAttr; + if (nStrPos >= nEnd) + { + break; + } + aAttrIter.NextPos(); + } + + if( !bLastNd || + ( ( !rWrt.m_bWriteClipboardDoc && !rWrt.m_bASCII_NoLastLineEnd ) + && !nStrPos && nEnd == nNodeEnd ) ) + rWrt.Strm().WriteUnicodeOrByteText( static_cast<SwASCWriter&>(rWrt).GetLineEnd()); + + return rWrt; +} + +/* + * Create the table for the ASCII function pointers to the output + * function. + * There are local structures that only need to be known to the ASCII DLL. + */ + +SwNodeFnTab aASCNodeFnTab = { +/* RES_TXTNODE */ OutASC_SwTextNode, +/* RES_GRFNODE */ nullptr, +/* RES_OLENODE */ nullptr +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ascii/parasc.cxx b/sw/source/filter/ascii/parasc.cxx new file mode 100644 index 0000000000..b4e191df62 --- /dev/null +++ b/sw/source/filter/ascii/parasc.cxx @@ -0,0 +1,521 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> + +#include <tools/stream.hxx> +#include <hintids.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/sfxsids.hrc> +#include <editeng/fontitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <svl/languageoptions.hxx> +#include <shellio.hxx> +#include <doc.hxx> +#include <IDocumentContentOperations.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <pam.hxx> +#include <breakit.hxx> +#include <swerror.h> +#include <strings.hrc> +#include <mdiexp.hxx> +#include <poolfmt.hxx> +#include <iodetect.hxx> + +#include <vcl/metric.hxx> +#include <osl/diagnose.h> + +#define ASC_BUFFLEN 4096 + +namespace { + +class SwASCIIParser +{ + SwDoc& m_rDoc; + std::optional<SwPaM> m_oPam; + SvStream& m_rInput; + std::unique_ptr<char[]> m_pArr; + const SwAsciiOptions& m_rOpt; + SwAsciiOptions m_usedAsciiOptions; + std::optional<SfxItemSet> m_oItemSet; + tools::Long m_nFileSize; + SvtScriptType m_nScript; + bool m_bNewDoc; + + ErrCode ReadChars(); + void InsertText( const OUString& rStr ); + + SwASCIIParser(const SwASCIIParser&) = delete; + SwASCIIParser& operator=(const SwASCIIParser&) = delete; + +public: + SwASCIIParser( SwDoc& rD, const SwPaM& rCursor, SvStream& rIn, + bool bReadNewDoc, const SwAsciiOptions& rOpts ); + + ErrCode CallParser(); + const SwAsciiOptions& GetUsedAsciiOptions() const { return m_usedAsciiOptions; } +}; + +} + +// Call for the general reader interface +ErrCodeMsg AsciiReader::Read( SwDoc& rDoc, const OUString&, SwPaM &rPam, const OUString & ) +{ + if( !m_pStream ) + { + OSL_ENSURE( false, "ASCII read without a stream" ); + return ERR_SWG_READ_ERROR; + } + + ErrCode nRet; + { + SwASCIIParser aParser( rDoc, rPam, *m_pStream, + !m_bInsertMode, m_aOption.GetASCIIOpts() ); + nRet = aParser.CallParser(); + + OUString optionsString; + aParser.GetUsedAsciiOptions().WriteUserData(optionsString); + + if(m_pMedium != nullptr) + m_pMedium->GetItemSet().Put(SfxStringItem(SID_FILE_FILTEROPTIONS, optionsString)); + } + // after Read reset the options + m_aOption.ResetASCIIOpts(); + return nRet; +} + +SwASCIIParser::SwASCIIParser(SwDoc& rD, const SwPaM& rCursor, SvStream& rIn, bool bReadNewDoc, + const SwAsciiOptions& rOpts) + : m_rDoc(rD) + , m_rInput(rIn) + , m_rOpt(rOpts) + , m_usedAsciiOptions(rOpts) + , m_nFileSize(0) + , m_nScript(SvtScriptType::NONE) + , m_bNewDoc(bReadNewDoc) +{ + m_oPam.emplace(*rCursor.GetPoint()); + m_pArr.reset(new char[ASC_BUFFLEN + 2]); + + m_oItemSet.emplace( + m_rDoc.GetAttrPool(), + svl::Items<RES_CHRATR_FONT, RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_FONT, + RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_LANGUAGE>); + + // set defaults from the options + if (m_rOpt.GetLanguage()) + { + SvxLanguageItem aLang(m_rOpt.GetLanguage(), RES_CHRATR_LANGUAGE); + m_oItemSet->Put(aLang); + aLang.SetWhich(RES_CHRATR_CJK_LANGUAGE); + m_oItemSet->Put(aLang); + aLang.SetWhich(RES_CHRATR_CTL_LANGUAGE); + m_oItemSet->Put(aLang); + } + if (m_rOpt.GetFontName().isEmpty()) + return; + + vcl::Font aTextFont(m_rOpt.GetFontName(), Size(0, 10)); + if (m_rDoc.getIDocumentDeviceAccess().getPrinter(false)) + aTextFont = m_rDoc.getIDocumentDeviceAccess().getPrinter(false)->GetFontMetric(aTextFont); + SvxFontItem aFont( aTextFont.GetFamilyType(), aTextFont.GetFamilyName(), + OUString(), aTextFont.GetPitch(), aTextFont.GetCharSet(), RES_CHRATR_FONT ); + m_oItemSet->Put(aFont); + aFont.SetWhich(RES_CHRATR_CJK_FONT); + m_oItemSet->Put(aFont); + aFont.SetWhich(RES_CHRATR_CTL_FONT); + m_oItemSet->Put(aFont); +} + +// Calling the parser +ErrCode SwASCIIParser::CallParser() +{ + m_rInput.ResetError(); + m_nFileSize = m_rInput.TellEnd(); + m_rInput.Seek(STREAM_SEEK_TO_BEGIN); + m_rInput.ResetError(); + + ::StartProgress(STR_STATSTR_W4WREAD, 0, m_nFileSize, m_rDoc.GetDocShell()); + + std::optional<SwPaM> pInsPam; + sal_Int32 nSttContent = 0; + if (!m_bNewDoc) + { + const SwNode& rTmp = m_oPam->GetPoint()->GetNode(); + pInsPam.emplace( rTmp, rTmp, SwNodeOffset(0), SwNodeOffset(-1) ); + nSttContent = m_oPam->GetPoint()->GetContentIndex(); + } + + SwTextFormatColl *pColl = nullptr; + + if (m_bNewDoc) + { + pColl = m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_HTML_PRE, + false); + if (!pColl) + pColl = m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, + false); + if (pColl) + m_rDoc.SetTextFormatColl(*m_oPam, pColl); + } + + ErrCode nError = ReadChars(); + + if (m_oItemSet) + { + // set only the attribute, for scanned scripts. + if (!(SvtScriptType::LATIN & m_nScript)) + { + m_oItemSet->ClearItem(RES_CHRATR_FONT); + m_oItemSet->ClearItem(RES_CHRATR_LANGUAGE); + } + if (!(SvtScriptType::ASIAN & m_nScript)) + { + m_oItemSet->ClearItem(RES_CHRATR_CJK_FONT); + m_oItemSet->ClearItem(RES_CHRATR_CJK_LANGUAGE); + } + if (!(SvtScriptType::COMPLEX & m_nScript)) + { + m_oItemSet->ClearItem(RES_CHRATR_CTL_FONT); + m_oItemSet->ClearItem(RES_CHRATR_CTL_LANGUAGE); + } + if (m_oItemSet->Count()) + { + if (m_bNewDoc) + { + if (pColl) + { + // Using the pool defaults for the font causes significant + // trouble for the HTML filter, because it is not able + // to export the pool defaults (or to be more precise: + // the HTML filter is not able to detect whether a pool + // default has changed or not. Even a comparison with the + // HTML template does not work, because the defaults are + // not copied when a new doc is created. The result of + // comparing pool defaults therefore would be that the + // defaults are exported always if the have changed for + // text documents in general. That's not sensible, as well + // as it is not sensible to export them always. + sal_uInt16 aWhichIds[4] = + { + RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CTL_FONT, 0 + }; + sal_uInt16 *pWhichIds = aWhichIds; + while (*pWhichIds) + { + const SfxPoolItem *pItem; + if (SfxItemState::SET + == m_oItemSet->GetItemState(*pWhichIds, false, &pItem)) + { + pColl->SetFormatAttr( *pItem ); + m_oItemSet->ClearItem(*pWhichIds); + } + ++pWhichIds; + } + } + if (m_oItemSet->Count()) + m_rDoc.SetDefault(*m_oItemSet); + } + else if( pInsPam ) + { + // then set over the insert range the defined attributes + *pInsPam->GetMark() = *m_oPam->GetPoint(); + pInsPam->GetPoint()->Assign(pInsPam->GetPoint()->GetNode(), SwNodeOffset(1), + nSttContent ); + + // !!!!! + OSL_ENSURE( false, "Have to change - hard attr. to para. style" ); + m_rDoc.getIDocumentContentOperations().InsertItemSet(*pInsPam, *m_oItemSet); + } + } + m_oItemSet.reset(); + } + + pInsPam.reset(); + + ::EndProgress(m_rDoc.GetDocShell()); + return nError; +} + +ErrCode SwASCIIParser::ReadChars() +{ + sal_Unicode *pStt = nullptr, *pEnd = nullptr, *pLastStt = nullptr; + tools::Long nReadCnt = 0, nLineLen = 0; + sal_Unicode cLastCR = 0; + bool bSwapUnicode = false; + + const SwAsciiOptions* pUseMe = &m_rOpt; + SwAsciiOptions aEmpty; + if (m_nFileSize >= 2 && aEmpty.GetFontName() == m_rOpt.GetFontName() + && aEmpty.GetCharSet() == m_rOpt.GetCharSet() + && aEmpty.GetLanguage() == m_rOpt.GetLanguage() + && aEmpty.GetParaFlags() == m_rOpt.GetParaFlags()) + { + sal_Size nLen, nOrig; + nOrig = nLen = m_rInput.ReadBytes(m_pArr.get(), ASC_BUFFLEN); + rtl_TextEncoding eCharSet; + LineEnd eLineEnd; + bool bHasBom; + const bool bRet + = SwIoSystem::IsDetectableText(m_pArr.get(), nLen, &eCharSet, + &bSwapUnicode, &eLineEnd, &bHasBom); + if (!bRet) + return ERRCODE_IO_BROKENPACKAGE; + + OSL_ENSURE(bRet, "Autodetect of text import without nag dialog must have failed"); + if (bRet && eCharSet != RTL_TEXTENCODING_DONTKNOW) + { + aEmpty.SetCharSet(eCharSet); + aEmpty.SetParaFlags(eLineEnd); + aEmpty.SetIncludeBOM(bHasBom); + m_rInput.SeekRel(-(tools::Long(nLen))); + } + else + m_rInput.SeekRel(-(tools::Long(nOrig))); + pUseMe=&aEmpty; + } + m_usedAsciiOptions = *pUseMe; + + rtl_TextToUnicodeConverter hConverter=nullptr; + rtl_TextToUnicodeContext hContext=nullptr; + rtl_TextEncoding currentCharSet = pUseMe->GetCharSet(); + if (RTL_TEXTENCODING_UCS2 != currentCharSet) + { + if( currentCharSet == RTL_TEXTENCODING_DONTKNOW ) + currentCharSet = RTL_TEXTENCODING_ASCII_US; + hConverter = rtl_createTextToUnicodeConverter( currentCharSet ); + OSL_ENSURE( hConverter, "no string convert available" ); + if (!hConverter) + return ErrCode(ErrCodeArea::Sw, ErrCodeClass::Read, 0); + bSwapUnicode = false; + hContext = rtl_createTextToUnicodeContext( hConverter ); + } + else if (pUseMe != &aEmpty) //Already successfully figured out type + { + m_rInput.StartReadingUnicodeText(currentCharSet); + bSwapUnicode = m_rInput.IsEndianSwap(); + } + + std::unique_ptr<sal_Unicode[]> aWork; + sal_Size nArrOffset = 0; + + do { + if( pStt >= pEnd ) + { + if( pLastStt != pStt ) + InsertText( OUString( pLastStt )); + + // Read a new block + sal_Size lGCount; + if (ERRCODE_NONE != m_rInput.GetError() + || 0 + == (lGCount = m_rInput.ReadBytes(m_pArr.get() + nArrOffset, + ASC_BUFFLEN - nArrOffset))) + break; // break from the while loop + + /* + If there was some unconverted bytes on the last cycle then they + were put at the beginning of the array, so total bytes available + to convert this cycle includes them. If we found 0 following bytes + then we ignore the previous partial character. + */ + lGCount += nArrOffset; + + if( hConverter ) + { + sal_uInt32 nInfo; + sal_Size nNewLen = lGCount, nCntBytes; + aWork.reset(new sal_Unicode[nNewLen + 1]); // add 1 for '\0' + sal_Unicode* pBuf = aWork.get(); + pBuf[nNewLen] = 0; // ensure '\0' + + nNewLen = rtl_convertTextToUnicode(hConverter, hContext, m_pArr.get(), lGCount, + pBuf, nNewLen, + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT + | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT + | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT + | RTL_TEXTTOUNICODE_FLAGS_GLOBAL_SIGNATURE), + &nInfo, &nCntBytes); + nArrOffset = lGCount - nCntBytes; + if( 0 != nArrOffset ) + memmove(m_pArr.get(), m_pArr.get() + nCntBytes, nArrOffset); + + pStt = pLastStt = aWork.get(); + pEnd = pStt + nNewLen; + } + else + { + pStt = pLastStt = reinterpret_cast<sal_Unicode*>(m_pArr.get()); + auto nChars = lGCount / 2; + pEnd = pStt + nChars; + + if( bSwapUnicode ) + { + char *pF = m_pArr.get(), *pN = m_pArr.get() + 1; + for (sal_Size n = 0; n < nChars; ++n, pF += 2, pN += 2) + { + char c = *pF; + *pF = *pN; + *pN = c; + } + } + } + + *pEnd = 0; + nReadCnt += lGCount; + + ::SetProgressState(nReadCnt, m_rDoc.GetDocShell()); + + if( cLastCR ) + { + if( 0x0a == *pStt && 0x0d == cLastCR ) + pLastStt = ++pStt; + cLastCR = 0; + nLineLen = 0; + // We skip the last one at the end + if (!m_rInput.eof() || !(pEnd == pStt || (!*pEnd && pEnd == pStt + 1))) + m_rDoc.getIDocumentContentOperations().SplitNode(*m_oPam->GetPoint(), false); + } + } + + bool bIns = true, bSplitNode = false; + switch( *pStt ) + { + + case 0x0a: if( LINEEND_LF == pUseMe->GetParaFlags() ) + { + bIns = false; + *pStt = 0; + ++pStt; + + // We skip the last one at the end + if (!m_rInput.eof() || pEnd != pStt) + bSplitNode = true; + } + break; + + case 0x0d: if( LINEEND_LF != pUseMe->GetParaFlags() ) + { + bIns = false; + *pStt = 0; + ++pStt; + + bool bChkSplit = true; + if( LINEEND_CRLF == pUseMe->GetParaFlags() ) + { + if( pStt == pEnd ) + { + cLastCR = 0x0d; + bChkSplit = false; + } + else if( 0x0a == *pStt ) + ++pStt; + } + + // We skip the last one at the end + if (bChkSplit && (!m_rInput.eof() || pEnd != pStt)) + bSplitNode = true; + } + break; + + case 0x0c: + { + // Insert a hard page break + *pStt++ = 0; + if( nLineLen ) + { + InsertText( OUString( pLastStt )); + } + m_rDoc.getIDocumentContentOperations().SplitNode(*m_oPam->GetPoint(), + false); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( + *m_oPam, SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK)); + pLastStt = pStt; + nLineLen = 0; + bIns = false; + } + break; + + case 0x1a: + if (nReadCnt == m_nFileSize && pStt + 1 == pEnd) + *pStt = 0; + else + *pStt = '#'; // Replacement visualisation + break; + + case '\t': break; + + default: + if( ' ' > *pStt ) + // Found control char, replace with '#' + *pStt = '#'; + break; + } + + if( bIns ) + { + if( ( nLineLen >= MAX_ASCII_PARA - 100 ) && + ( ( *pStt == ' ' ) || ( nLineLen >= MAX_ASCII_PARA - 1 ) ) ) + { + sal_Unicode c = *pStt; + *pStt = 0; + InsertText( OUString( pLastStt )); + m_rDoc.getIDocumentContentOperations().SplitNode(*m_oPam->GetPoint(), false); + pLastStt = pStt; + nLineLen = 0; + *pStt = c; + } + ++pStt; + ++nLineLen; + } + else if( bSplitNode ) + { + // We found a CR/LF, thus save the text + InsertText( OUString( pLastStt )); + if (m_bNewDoc) + m_rDoc.getIDocumentContentOperations().AppendTextNode(*m_oPam->GetPoint()); + else + m_rDoc.getIDocumentContentOperations().SplitNode(*m_oPam->GetPoint(), false); + pLastStt = pStt; + nLineLen = 0; + } + } while(true); + + if( hConverter ) + { + rtl_destroyTextToUnicodeContext( hConverter, hContext ); + rtl_destroyTextToUnicodeConverter( hConverter ); + } + return ERRCODE_NONE; +} + +void SwASCIIParser::InsertText( const OUString& rStr ) +{ + m_rDoc.getIDocumentContentOperations().InsertString(*m_oPam, rStr); + + if (m_oItemSet && g_pBreakIt + && m_nScript != (SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX)) + m_nScript |= g_pBreakIt->GetAllScriptsOfText(rStr); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ascii/wrtasc.cxx b/sw/source/filter/ascii/wrtasc.cxx new file mode 100644 index 0000000000..0f1e368b92 --- /dev/null +++ b/sw/source/filter/ascii/wrtasc.cxx @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/endian.h> +#include <tools/stream.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <ndtxt.hxx> +#include <mdiexp.hxx> +#include <fmtcntnt.hxx> +#include <frmfmt.hxx> +#include "wrtasc.hxx" +#include <frameformats.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxsids.hrc> +#include <o3tl/string_view.hxx> + +#include <strings.hrc> + +SwASCWriter::SwASCWriter( std::u16string_view rFltNm ) +{ + SwAsciiOptions aNewOpts; + + switch( 5 <= rFltNm.size() ? rFltNm[4] : 0 ) + { + case 'D': + aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_850 ); + aNewOpts.SetParaFlags( LINEEND_CRLF ); + if( 5 < rFltNm.size() ) + { + std::u16string_view aFilterNum = rFltNm.substr( 5 ); + switch( o3tl::toInt32(aFilterNum) ) + { + case 437: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_437 ); break; + case 850: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_850 ); break; + case 860: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_860 ); break; + case 861: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_861 ); break; + case 863: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_863 ); break; + case 865: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_865 ); break; + } + } + break; + + case 'A': +#ifndef _WIN32 + aNewOpts.SetCharSet( RTL_TEXTENCODING_MS_1252 ); + aNewOpts.SetParaFlags( LINEEND_CRLF ); +#endif + break; + + case 'M': + aNewOpts.SetCharSet( RTL_TEXTENCODING_APPLE_ROMAN ); + aNewOpts.SetParaFlags( LINEEND_CR ); + break; + + case 'X': +#ifdef _WIN32 + aNewOpts.SetCharSet( RTL_TEXTENCODING_MS_1252 ); + aNewOpts.SetParaFlags( LINEEND_LF ); +#endif + break; + + default: + if( rFltNm.size() >= 4 && rFltNm.substr( 4 )==u"_DLG" ) + { + // use the options + aNewOpts = GetAsciiOptions(); + } + } + SetAsciiOptions( aNewOpts ); +} + +SwASCWriter::~SwASCWriter() {} + +ErrCode SwASCWriter::WriteStream() +{ + static constexpr OUString STR_CR = u"\015"_ustr; + static constexpr OUStringLiteral STR_LF = u"\012"; + static constexpr OUStringLiteral STR_CRLF = u"\015\012"; + static constexpr OUStringLiteral STR_BLANK = u" "; + bool bIncludeBOM = GetAsciiOptions().GetIncludeBOM(); + bool bIncludeHidden = GetAsciiOptions().GetIncludeHidden(); + + if( m_bASCII_ParaAsCR ) // If predefined + m_sLineEnd = STR_CR; + else if( m_bASCII_ParaAsBlank ) + m_sLineEnd = STR_BLANK; + else + switch( GetAsciiOptions().GetParaFlags() ) + { + case LINEEND_CR: m_sLineEnd = STR_CR; break; + case LINEEND_LF: m_sLineEnd = STR_LF; break; + case LINEEND_CRLF: m_sLineEnd = STR_CRLF; break; + } + + SwNodeOffset nMaxNode = m_pDoc->GetNodes().Count(); + + if( m_bShowProgress ) + ::StartProgress( STR_STATSTR_W4WWRITE, 0, sal_Int32(nMaxNode), m_pDoc->GetDocShell() ); + + SwPaM* pPam = m_pOrigPam; + + bool bWriteSttTag = m_bUCS2_WithStartChar && + (RTL_TEXTENCODING_UCS2 == GetAsciiOptions().GetCharSet() || + RTL_TEXTENCODING_UTF8 == GetAsciiOptions().GetCharSet()); + + rtl_TextEncoding eOld = Strm().GetStreamCharSet(); + Strm().SetStreamCharSet( GetAsciiOptions().GetCharSet() ); + + // Output all areas of the pam into the ASC file + do { + bool bTstFly = true; + while( m_pCurrentPam->GetPoint()->GetNodeIndex() < m_pCurrentPam->GetMark()->GetNodeIndex() || + (m_pCurrentPam->GetPoint()->GetNodeIndex() == m_pCurrentPam->GetMark()->GetNodeIndex() && + m_pCurrentPam->GetPoint()->GetContentIndex() <= m_pCurrentPam->GetMark()->GetContentIndex()) ) + { + SwTextNode* pNd = m_pCurrentPam->GetPoint()->GetNode().GetTextNode(); + if( pNd ) + { + // Should we have frames only? + // That's possible, if we put a frame selection into the clipboard + if( bTstFly && m_bWriteAll && + pNd->GetText().isEmpty() && + // Frame exists + !m_pDoc->GetSpzFrameFormats()->empty() && + // Only one node in the array + m_pDoc->GetNodes().GetEndOfExtras().GetIndex() + 3 == + m_pDoc->GetNodes().GetEndOfContent().GetIndex() && + // And exactly this one is selected + m_pDoc->GetNodes().GetEndOfContent().GetIndex() - 1 == + m_pCurrentPam->GetPoint()->GetNodeIndex() ) + { + // Print the frame's content. + // It is always at position 0! + const SwFrameFormat* pFormat = (*m_pDoc->GetSpzFrameFormats())[ 0 ]; + const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx(); + if( pIdx ) + { + m_pCurrentPam = NewUnoCursor(*m_pDoc, pIdx->GetIndex(), + pIdx->GetNode().EndOfSectionIndex() ); + m_pCurrentPam->Exchange(); + continue; // reset while loop! + } + } + else if (!pNd->IsHidden() || bIncludeHidden) + { + if (bWriteSttTag) + { + switch(GetAsciiOptions().GetCharSet()) + { + case RTL_TEXTENCODING_UTF8: + if( bIncludeBOM ) + { + Strm().WriteUChar( 0xEF ).WriteUChar( 0xBB ).WriteUChar( 0xBF ); + } + + break; + case RTL_TEXTENCODING_UCS2: +#ifdef OSL_LITENDIAN + Strm().SetEndian(SvStreamEndian::LITTLE); +#else + Strm().SetEndian(SvStreamEndian::BIG); +#endif + if( bIncludeBOM ) + { + Strm().StartWritingUnicodeText(); + } + break; + + } + bWriteSttTag = false; + } + Out( aASCNodeFnTab, *pNd, *this ); + } + bTstFly = false; // Testing once is enough + } + + if( !m_pCurrentPam->Move( fnMoveForward, GoInNode ) ) + break; + + if( m_bShowProgress ) + ::SetProgressState( sal_Int32(m_pCurrentPam->GetPoint()->GetNodeIndex()), + m_pDoc->GetDocShell() ); // How far? + + } + } while( CopyNextPam( &pPam ) ); // Until all pams are processed + + Strm().SetStreamCharSet( eOld ); + + if( m_bShowProgress ) + ::EndProgress( m_pDoc->GetDocShell() ); + + return ERRCODE_NONE; +} + +void SwASCWriter::SetupFilterOptions(SfxMedium& rMedium) +{ + if( const SfxStringItem* pItem = rMedium.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) ) + { + SwAsciiOptions aOpt; + OUString sItemOpt; + sItemOpt = pItem->GetValue(); + aOpt.ReadUserData(sItemOpt); + SetAsciiOptions(aOpt); + } +} + +void GetASCWriter( + std::u16string_view rFltNm, [[maybe_unused]] const OUString& /*rBaseURL*/, WriterRef& xRet ) +{ + xRet = new SwASCWriter( rFltNm ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ascii/wrtasc.hxx b/sw/source/filter/ascii/wrtasc.hxx new file mode 100644 index 0000000000..c2e3dfa9a2 --- /dev/null +++ b/sw/source/filter/ascii/wrtasc.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_SW_SOURCE_FILTER_ASCII_WRTASC_HXX +#define INCLUDED_SW_SOURCE_FILTER_ASCII_WRTASC_HXX + +#include <shellio.hxx> +#include <wrt_fn.hxx> + +extern SwNodeFnTab aASCNodeFnTab; + +// The ASC writer + +class SwASCWriter : public Writer +{ + OUString m_sLineEnd; + + virtual ErrCode WriteStream() override; + +public: + SwASCWriter(std::u16string_view rFilterName); + virtual ~SwASCWriter() override; + + void SetupFilterOptions(SfxMedium& rMedium) override; + const OUString& GetLineEnd() const { return m_sLineEnd; } +}; + +#endif // _ INCLUDED_SW_SOURCE_FILTER_ASCII_WRTASC_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |