sw/CppunitTest_sw_filter_ascii.mk |   76 ++++++++++++++++++
 sw/Module_sw.mk                   |    1 
 sw/qa/filter/ascii/ascii.cxx      |  153 ++++++++++++++++++++++++++++++++++++++
 sw/source/filter/ascii/wrtasc.cxx |   59 ++++++++++++++
 sw/source/filter/ascii/wrtasc.hxx |    1 
 5 files changed, 289 insertions(+), 1 deletion(-)

New commits:
commit 3e7de6b1bc9f28ba6b69489e7f5c7dc645abc695
Author:     Adam Seskunas <adamsesku...@gmail.com>
AuthorDate: Thu Mar 14 06:09:08 2024 -0700
Commit:     Hossein <hoss...@libreoffice.org>
CommitDate: Tue May 14 18:56:44 2024 +0200

    tdf#144576 Copy a table from Writer to plain text editor as a Matrix
    
    sw ascii filter
     - check for table nodes, output them seperately with formating
       to be displayed as a matrix when copy/pasted to a text file
    
    sw qa filter ascii
     - add new test suite along with test to check for correct output
    
    Change-Id: I8ca31bced3860e8e9752db8530ea6caaf31f2e5e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164833
    Reviewed-by: Hossein <hoss...@libreoffice.org>
    Tested-by: Jenkins

diff --git a/sw/CppunitTest_sw_filter_ascii.mk 
b/sw/CppunitTest_sw_filter_ascii.mk
new file mode 100644
index 000000000000..85a648c8ec69
--- /dev/null
+++ b/sw/CppunitTest_sw_filter_ascii.mk
@@ -0,0 +1,76 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# 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/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,sw_filter_ascii))
+
+$(eval $(call gb_CppunitTest_use_common_precompiled_header,sw_filter_ascii))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sw_filter_ascii, \
+    sw/qa/filter/ascii/ascii \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sw_filter_ascii, \
+    comphelper \
+    cppu \
+    cppuhelper \
+    editeng \
+    sal \
+    sfx \
+    subsequenttest \
+    svl \
+    svx \
+    svxcore \
+    sw \
+    swqahelper \
+    test \
+    unotest \
+    utl \
+    vcl \
+    tl \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,sw_filter_ascii,\
+    boost_headers \
+    libxml2 \
+))
+
+$(eval $(call gb_CppunitTest_set_include,sw_filter_ascii,\
+    -I$(SRCDIR)/sw/inc \
+    -I$(SRCDIR)/sw/source/core/inc \
+    -I$(SRCDIR)/sw/source/uibase/inc \
+    -I$(SRCDIR)/sw/qa/inc \
+    $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_api,sw_filter_ascii,\
+       udkapi \
+       offapi \
+       oovbaapi \
+))
+
+$(eval $(call gb_CppunitTest_use_ure,sw_filter_ascii))
+$(eval $(call gb_CppunitTest_use_vcl,sw_filter_ascii))
+
+$(eval $(call gb_CppunitTest_use_rdb,sw_filter_ascii,services))
+
+$(eval $(call gb_CppunitTest_use_custom_headers,sw_filter_ascii,\
+    officecfg/registry \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,sw_filter_ascii))
+
+$(eval $(call gb_CppunitTest_use_uiconfigs,sw_filter_ascii, \
+    modules/swriter \
+))
+
+$(eval $(call gb_CppunitTest_use_more_fonts,sw_filter_ascii))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sw/Module_sw.mk b/sw/Module_sw.mk
index fc2afe9fb7d9..2db6fbace5f5 100644
--- a/sw/Module_sw.mk
+++ b/sw/Module_sw.mk
@@ -167,6 +167,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sw,\
     CppunitTest_sw_filter_ww8 \
     CppunitTest_sw_filter_html \
     CppunitTest_sw_filter_xml \
+    CppunitTest_sw_filter_ascii \
     CppunitTest_sw_a11y \
     CppunitTest_sw_core_theme \
     CppunitTest_sw_pdf_test \
diff --git a/sw/qa/filter/ascii/ascii.cxx b/sw/qa/filter/ascii/ascii.cxx
new file mode 100644
index 000000000000..d6c4773ca40f
--- /dev/null
+++ b/sw/qa/filter/ascii/ascii.cxx
@@ -0,0 +1,153 @@
+/* -*- 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/.
+ */
+
+#include <swmodeltestbase.hxx>
+
+#include <unotxdoc.hxx>
+#include <itabenum.hxx>
+#include <wrtsh.hxx>
+#include <com/sun/star/text/XTextTable.hpp>
+#include <ndtxt.hxx>
+#include <swdtflvr.hxx>
+#include <swmodule.hxx>
+
+namespace
+{
+/**
+ * Covers sw/source/filter/ascii/ fixes.
+ *
+ * Note that these tests are meant to be simple: either load a file and assert 
some result or build
+ * a document model with code, export and assert that result.
+ *
+ * Keep using the various sw_<format>import/export suites for multiple filter 
calls inside a single
+ * test.
+ */
+class Test : public SwModelTestBase
+{
+public:
+    Test()
+        : SwModelTestBase("/sw/qa/filter/ascii/data/")
+    {
+    }
+};
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf144576_ascii)
+{
+    // Given a document with a 2x2 table
+    createSwDoc();
+    SwDoc* pDoc = getSwDoc();
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    SwInsertTableOptions aTableOptions(SwInsertTableFlags::DefaultBorder, 0);
+    pWrtShell->InsertTable(aTableOptions, /*nRows=*/2, /*nCols=*/2);
+
+    uno::Reference<text::XTextTablesSupplier> xTablesSupplier(mxComponent, 
uno::UNO_QUERY);
+    uno::Reference<container::XNameAccess> xTableNames = 
xTablesSupplier->getTextTables();
+    CPPUNIT_ASSERT(xTableNames->hasByName("Table1"));
+    uno::Reference<text::XTextTable> xTable1(xTableNames->getByName("Table1"), 
uno::UNO_QUERY);
+
+    pWrtShell->GotoTable(u"Table1"_ustr);
+    pWrtShell->Insert(u"A"_ustr);
+    pWrtShell->GoNextCell(false);
+    pWrtShell->Insert(u"B"_ustr);
+    pWrtShell->GoNextCell(false);
+    pWrtShell->Insert(u"C"_ustr);
+    pWrtShell->GoNextCell(false);
+    pWrtShell->Insert(u"D"_ustr);
+
+    // Select the whole table
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+
+    rtl::Reference<SwTransferable> xTransferable(new 
SwTransferable(*pWrtShell));
+    xTransferable->Copy(); // Ctl-C
+    xTransferable.get();
+
+    // Get the plain text version of the selection
+    datatransfer::DataFlavor aFlavor;
+    aFlavor.MimeType = "text/plain;charset=utf-16";
+    aFlavor.DataType = cppu::UnoType<OUString>::get();
+    uno::Any aData = xTransferable->getTransferData(aFlavor);
+    OUString aActual;
+    aData >>= aActual;
+    pWrtShell->ClearMark();
+
+    CPPUNIT_ASSERT(aData.hasValue());
+
+    OUString aExpected = u"A   B"_ustr SAL_NEWLINE_STRING u"C  D"_ustr 
SAL_NEWLINE_STRING u""_ustr;
+
+    // Without the fix in place, the test fails with:
+    // - Expected: A    B
+    //             C    D
+    //
+    // - Actual  : A
+    //             B
+    //             C
+    //             D
+    // i.e. Each cell is seperated by a tab
+    CPPUNIT_ASSERT_EQUAL(aExpected, aActual);
+
+    // Add some newlines in the first two cells
+    // and test to see if they're handled correctly
+    uno::Reference<text::XTextRange> xCellA1(xTable1->getCellByName("A1"), 
uno::UNO_QUERY);
+    xCellA1->setString(u""_ustr);
+    uno::Reference<text::XTextRange> xCellB1(xTable1->getCellByName("B1"), 
uno::UNO_QUERY);
+    xCellB1->setString(u""_ustr);
+
+    pWrtShell->GotoTable(u"Table1"_ustr);
+    pWrtShell->Insert(u"A"_ustr);
+    pWrtShell->SplitNode();
+    pWrtShell->Insert(u"A1"_ustr);
+    pWrtShell->GoNextCell(false);
+    pWrtShell->Insert(u"B"_ustr);
+    pWrtShell->SplitNode();
+    pWrtShell->Insert(u"B1"_ustr);
+    pWrtShell->SplitNode();
+    pWrtShell->Insert(u"B2"_ustr);
+
+    aExpected
+        = u"A"_ustr SAL_NEWLINE_STRING u"A1    B"_ustr SAL_NEWLINE_STRING 
u"B1"_ustr SAL_NEWLINE_STRING u"B2"_ustr SAL_NEWLINE_STRING u"C      D"_ustr 
SAL_NEWLINE_STRING u""_ustr;
+
+    // Select the whole table
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+    dispatchCommand(mxComponent, ".uno:SelectAll", {});
+
+    // Get the plain text version of the selection
+    rtl::Reference<SwTransferable> xNewTransferable(new 
SwTransferable(*pWrtShell));
+    xNewTransferable->Copy(); // Ctl-C
+    xNewTransferable.get();
+    aData = xNewTransferable->getTransferData(aFlavor);
+    CPPUNIT_ASSERT(aData.hasValue());
+    aData >>= aActual;
+
+    // Without the fix in place, the test fails with:
+    // - Expected: A
+    //             A1   B
+    //             B1
+    //             B2
+    //             C    D
+    //
+    // - Actual  : A
+    //             A1
+    //             B
+    //             B1
+    //             B2
+    //             C
+    //             D
+    // i.e. Each cell is seperated by a tab, a newline inside
+    // a cell gets written to the next line in the furthest
+    // left spot available
+    CPPUNIT_ASSERT_EQUAL(aExpected, aActual);
+}
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ascii/wrtasc.cxx 
b/sw/source/filter/ascii/wrtasc.cxx
index 0f1e368b928e..39f5ec9cfb91 100644
--- a/sw/source/filter/ascii/wrtasc.cxx
+++ b/sw/source/filter/ascii/wrtasc.cxx
@@ -186,7 +186,14 @@ ErrCode SwASCWriter::WriteStream()
                         }
                         bWriteSttTag = false;
                     }
-                    Out( aASCNodeFnTab, *pNd, *this );
+
+                    SwTableNode* pTableNd = pNd->FindTableNode();
+
+                    // Handle a table
+                    if (pTableNd && m_bWriteAll)
+                        WriteTable(pTableNd, pNd);
+                    else
+                        Out( aASCNodeFnTab, *pNd, *this );
                 }
                 bTstFly = false;        // Testing once is enough
             }
@@ -221,6 +228,56 @@ void SwASCWriter::SetupFilterOptions(SfxMedium& rMedium)
     }
 }
 
+void SwASCWriter::WriteTable(SwTableNode* pTableNd, SwTextNode* pNd)
+{
+    OUString sPreLineEnd = this->m_sLineEnd;
+    m_sLineEnd = u""_ustr;
+
+    const SwTableLine* pEndTabLine = pTableNd->GetTable().GetTabLines().back();
+    const SwTableBox* pEndTabBox = pEndTabLine->GetTabBoxes().back();
+
+    for( const SwTableLine* pLine : pTableNd->GetTable().GetTabLines() )
+    {
+        for( const SwTableBox* pBox : pLine->GetTabBoxes() )
+        {
+            Out( aASCNodeFnTab, *pNd, *this );
+
+            Point aPrevBoxPoint = pNd->GetTableBox()->GetCoordinates();
+            m_pCurrentPam->Move(fnMoveForward, GoInNode);
+            pNd = m_pCurrentPam->GetPoint()->GetNode().GetTextNode();
+
+            // Line break in a box
+            // Each line is a new SwTextNode so we
+            // need to parse inside the current box
+            while (pNd->GetTableBox() && (pNd->GetTableBox()->GetCoordinates() 
== aPrevBoxPoint))
+            {
+                Strm().WriteUnicodeOrByteText(sPreLineEnd);
+                Out(aASCNodeFnTab, *pNd, *this);
+
+                m_pCurrentPam->Move(fnMoveForward, GoInNode);
+                pNd = m_pCurrentPam->GetPoint()->GetNode().GetTextNode();
+            }
+            if (pBox != pLine->GetTabBoxes().back())
+                Strm().WriteUChar( 0x9 );
+
+            if (pBox == pEndTabBox)
+                this->m_sLineEnd = sPreLineEnd;
+
+        }// end for each Box
+
+        if (pLine == pEndTabLine)
+        {
+            m_pCurrentPam->Move(fnMoveBackward, GoInNode);
+            pNd = m_pCurrentPam->GetPoint()->GetNode().GetTextNode();
+            Strm().WriteUnicodeOrByteText( sPreLineEnd );
+        }
+        if (pLine != pEndTabLine)
+            Strm().WriteUnicodeOrByteText( sPreLineEnd );
+
+    }// end For each row
+    this->m_sLineEnd = sPreLineEnd;
+}
+
 void GetASCWriter(
     std::u16string_view rFltNm, [[maybe_unused]] const OUString& /*rBaseURL*/, 
WriterRef& xRet )
 {
diff --git a/sw/source/filter/ascii/wrtasc.hxx 
b/sw/source/filter/ascii/wrtasc.hxx
index c2e3dfa9a2d4..7464c4eccd52 100644
--- a/sw/source/filter/ascii/wrtasc.hxx
+++ b/sw/source/filter/ascii/wrtasc.hxx
@@ -31,6 +31,7 @@ class SwASCWriter : public Writer
     OUString m_sLineEnd;
 
     virtual ErrCode WriteStream() override;
+    void WriteTable(SwTableNode* pTableNd, SwTextNode* pNd);
 
 public:
     SwASCWriter(std::u16string_view rFilterName);

Reply via email to