Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/sw/source/core/docnode/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 166 kB image not shown  

Quelle  ndtbl.cxx   Sprache: C

 
/* -*- 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 <libxml/xmlwriter.h>
#include <config_wasm_strip.h>
#include <memory>
#include <fesh.hxx>
#include <hintids.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/protitem.hxx>
#include <editeng/boxitem.hxx>
#include <svl/stritem.hxx>
#include <editeng/shaditem.hxx>
#include <fmtfsize.hxx>
#include <fmtornt.hxx>
#include <fmtfordr.hxx>
#include <fmtpdsc.hxx>
#include <fmtanchr.hxx>
#include <fmtlsplt.hxx>
#include <frmatr.hxx>
#include <cellfrm.hxx>
#include <pagefrm.hxx>
#include <tabcol.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <UndoManager.hxx>
#include <DocumentSettingManager.hxx>
#include <IDocumentChartDataProviderAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentStylePoolAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentState.hxx>
#include <cntfrm.hxx>
#include <pam.hxx>
#include <swcrsr.hxx>
#include <swmodule.hxx>
#include <swtable.hxx>
#include <swundo.hxx>
#include <tblsel.hxx>
#include <poolfmt.hxx>
#include <tabfrm.hxx>
#include <UndoCore.hxx>
#include <UndoRedline.hxx>
#include <UndoDelete.hxx>
#include <UndoNumbering.hxx>
#include <UndoTable.hxx>
#include <hints.hxx>
#include <tblafmt.hxx>
#include <frminf.hxx>
#include <cellatr.hxx>
#include <swtblfmt.hxx>
#include <swddetbl.hxx>
#include <mvsave.hxx>
#include <docary.hxx>
#include <redline.hxx>
#include <rolbck.hxx>
#include <tblrwcl.hxx>
#include <editsh.hxx>
#include <txtfrm.hxx>
#include <section.hxx>
#include <frmtool.hxx>
#include <node2lay.hxx>
#include <strings.hrc>
#include <docsh.hxx>
#include <unochart.hxx>
#include <node.hxx>
#include <ndtxt.hxx>
#include <cstdlib>
#include <map>
#include <algorithm>
#include <rootfrm.hxx>
#include <fldupde.hxx>
#include <calbck.hxx>
#include <fntcache.hxx>
#include <frameformats.hxx>
#include <officecfg/Office/Writer.hxx>
#include <o3tl/numeric.hxx>
#include <o3tl/string_view.hxx>
#include <svl/numformat.hxx>
#include <tools/datetimeutils.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>

#ifdef DBG_UTIL
#define CHECK_TABLE(t) (t).CheckConsistency();
#else
#define CHECK_TABLE(t)
#endif

using ::editeng::SvxBorderLine;
using namespace ::com::sun::star;

const sal_Unicode T2T_PARA = 0x0a;

static void lcl_SetDfltBoxAttr( SwFrameFormat& rFormat, sal_uInt8 nId )
{
    bool bTop = false, bBottom = false, bLeft = false, bRight = false;
    switch ( nId )
    {
    case 0: bTop = bBottom = bLeft = true;          break;
    case 1: bTop = bBottom = bLeft = bRight = truebreak;
    case 2: bBottom = bLeft = true;                 break;
    case 3: bBottom = bLeft = bRight = true;        break;
    }

    const bool bHTML = rFormat.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE);
    Color aCol( bHTML ? COL_GRAY : COL_BLACK );
    // Default border in Writer: 0.5pt (matching Word)
    SvxBorderLine aLine( &aCol, SvxBorderLineWidth::VeryThin );
    if ( bHTML )
    {
        aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
    }
    SvxBoxItem aBox(RES_BOX);
    aBox.SetAllDistances(55);
    if ( bTop )
        aBox.SetLine( &aLine, SvxBoxItemLine::TOP );
    if ( bBottom )
        aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
    if ( bLeft )
        aBox.SetLine( &aLine, SvxBoxItemLine::LEFT );
    if ( bRight )
        aBox.SetLine( &aLine, SvxBoxItemLine::RIGHT );
    rFormat.SetFormatAttr( aBox );
}

typedef std::map<SwFrameFormat *, SwTableBoxFormat *> DfltBoxAttrMap_t;
typedef std::vector<DfltBoxAttrMap_t *> DfltBoxAttrList_t;

static void
lcl_SetDfltBoxAttr(SwTableBox& rBox, DfltBoxAttrList_t & rBoxFormatArr,
        sal_uInt8 const nId, SwTableAutoFormat const*const pAutoFormat = nullptr)
{
    DfltBoxAttrMap_t * pMap = rBoxFormatArr[ nId ];
    if (!pMap)
    {
        pMap = new DfltBoxAttrMap_t;
        rBoxFormatArr[ nId ] = pMap;
    }

    SwTableBoxFormat* pNewTableBoxFormat = nullptr;
    SwFrameFormat* pBoxFrameFormat = rBox.GetFrameFormat();
    DfltBoxAttrMap_t::iterator const iter(pMap->find(pBoxFrameFormat));
    if (pMap->end() != iter)
    {
        pNewTableBoxFormat = iter->second;
    }
    else
    {
        SwDoc& rDoc = pBoxFrameFormat->GetDoc();
        // format does not exist, so create it
        pNewTableBoxFormat = rDoc.MakeTableBoxFormat();
        pNewTableBoxFormat->SetFormatAttr( pBoxFrameFormat->GetAttrSet().Get( RES_FRM_SIZE ) );

        if( pAutoFormat )
            pAutoFormat->UpdateToSet( nId, falsefalse,
                                    const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pNewTableBoxFormat->GetAttrSet())),
                                    SwTableAutoFormatUpdateFlags::Box,
                                    rDoc.GetNumberFormatter() );
        else
            ::lcl_SetDfltBoxAttr( *pNewTableBoxFormat, nId );

        (*pMap)[pBoxFrameFormat] = pNewTableBoxFormat;
    }
    rBox.ChgFrameFormat( pNewTableBoxFormat );
}

static SwTableBoxFormat *lcl_CreateDfltBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
                                    sal_uInt16 nCols, sal_uInt8 nId )
{
    if ( !rBoxFormatArr[nId] )
    {
        SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
        if( USHRT_MAX != nCols )
            pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
                                            USHRT_MAX / nCols, 0 ));
        ::lcl_SetDfltBoxAttr( *pBoxFormat, nId );
        rBoxFormatArr[ nId ] = pBoxFormat;
    }
    return rBoxFormatArr[nId];
}

static SwTableBoxFormat *lcl_CreateAFormatBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
                                    const SwTableAutoFormat& rAutoFormat,
                                    const sal_uInt16 nRows, const sal_uInt16 nCols, sal_uInt8 nId )
{
    if( !rBoxFormatArr[nId] )
    {
        SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
        rAutoFormat.UpdateToSet( nId, nRows==1, nCols==1,
                                const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pBoxFormat->GetAttrSet())),
                                SwTableAutoFormatUpdateFlags::Box,
                                rDoc.GetNumberFormatter( ) );
        if( USHRT_MAX != nCols )
            pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
                                            USHRT_MAX / nCols, 0 ));
        rBoxFormatArr[ nId ] = pBoxFormat;
    }
    return rBoxFormatArr[nId];
}

SwTableNode* SwDoc::IsIdxInTable( const SwNodeIndex& rIdx ) { return IsInTable(rIdx.GetNode()); }

SwTableNode* SwDoc::IsInTable(const SwNode& rIdx)
{
    SwNode* pNd = const_cast<SwNode*>(&rIdx);
    do {
        pNd = pNd->StartOfSectionNode();
        SwTableNode* pTableNd = pNd->GetTableNode();
        if( pTableNd )
            return pTableNd;
    } while ( pNd->GetIndex() );
    return nullptr;
}

/**
 * Insert a new Box before the InsPos
 */

bool SwNodes::InsBoxen( SwTableNode* pTableNd,
                        SwTableLine* pLine,
                        SwTableBoxFormat* pBoxFormat,
                        SwTextFormatColl* pTextColl,
                        const SfxItemSet* pAutoAttr,
                        sal_uInt16 nInsPos,
                        sal_uInt16 nCnt )
{
    if( !nCnt )
        return false;
    OSL_ENSURE( pLine, "No valid Line" );

    // Move Index after the Line's last Box
    SwNodeOffset nIdxPos(0);
    SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr;
    if( !pLine->GetTabBoxes().empty() )
    {
        if( nInsPos < pLine->GetTabBoxes().size() )
        {
            pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable(),
                            pLine->GetTabBoxes()[ nInsPos ] );
            if( nullptr == pPrvBox )
                pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
        }
        else
        {
            pNxtBox = pLine->FindNextBox( pTableNd->GetTable(),
                            pLine->GetTabBoxes().back() );
            if( nullptr == pNxtBox )
                pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
        }
    }
    else
    {
        pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
        if( nullptr == pNxtBox )
            pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
    }

    if( !pPrvBox && !pNxtBox )
    {
        bool bSetIdxPos = true;
        if( !pTableNd->GetTable().GetTabLines().empty() && !nInsPos )
        {
            const SwTableLine* pTableLn = pLine;
            while( pTableLn->GetUpper() )
                pTableLn = pTableLn->GetUpper()->GetUpper();

            if( pTableNd->GetTable().GetTabLines()[ 0 ] == pTableLn )
            {
                // Before the Table's first Box
                while( !( pNxtBox = pLine->GetTabBoxes()[0])->GetTabLines().empty() )
                    pLine = pNxtBox->GetTabLines()[0];
                nIdxPos = pNxtBox->GetSttIdx();
                bSetIdxPos = false;
            }
        }
        if( bSetIdxPos )
            // Tables without content or at the end; move before the End
            nIdxPos = pTableNd->EndOfSectionIndex();
    }
    else if( pNxtBox ) // There is a successor
        nIdxPos = pNxtBox->GetSttIdx();
    else // There is a predecessor
        nIdxPos = pPrvBox->GetSttNd()->EndOfSectionIndex() + 1;

    SwNodeIndex aEndIdx( *this, nIdxPos );
    for( sal_uInt16 n = 0; n < nCnt; ++n )
    {
        SwStartNode* pSttNd = new SwStartNode( aEndIdx.GetNode(), SwNodeType::Start,
                                                SwTableBoxStartNode );
        pSttNd->m_pStartOfSection = pTableNd;
        new SwEndNode( aEndIdx.GetNode(), *pSttNd );

        pPrvBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );

        SwTableBoxes & rTabBoxes = pLine->GetTabBoxes();
        sal_uInt16 nRealInsPos = nInsPos + n;
        if (nRealInsPos > rTabBoxes.size())
            nRealInsPos = rTabBoxes.size();

        rTabBoxes.insert( rTabBoxes.begin() + nRealInsPos, pPrvBox );

        if( ! pTextColl->IsAssignedToListLevelOfOutlineStyle()
            && RES_CONDTXTFMTCOLL != pTextColl->Which()
        )
            new SwTextNode( *pSttNd->EndOfSectionNode(), pTextColl, pAutoAttr );
        else
        {
            // Handle Outline numbering correctly!
            SwTextNode* pTNd = new SwTextNode(
                            *pSttNd->EndOfSectionNode(),
                            GetDoc().GetDfltTextFormatColl(),
                            pAutoAttr );
            pTNd->ChgFormatColl( pTextColl );
        }
    }
    return true;
}

/**
 * Insert a new Table
 */

const SwTable* SwDoc::InsertTable( const SwInsertTableOptions& rInsTableOpts,
                                   const SwPosition& rPos, sal_uInt16 nRows,
                                   sal_uInt16 nCols, sal_Int16 eAdjust,
                                   const SwTableAutoFormat* pTAFormat,
                                   const std::vector<sal_uInt16> *pColArr,
                                   bool bCalledFromShell,
                                   bool bNewModel,
                                   const OUString& rTableName )
{
    assert(nRows && "Table without line?");
    assert(nCols && "Table without rows?");

    {
        // Do not copy into Footnotes!
        if( rPos.GetNode() < GetNodes().GetEndOfInserts() &&
            rPos.GetNode().GetIndex() >= GetNodes().GetEndOfInserts().StartOfSectionIndex() )
            return nullptr;

        // If the ColumnArray has a wrong count, ignore it!
        if( pColArr &&
            static_cast<size_t>(nCols) + ( text::HoriOrientation::NONE == eAdjust ? 2 : 1 ) != pColArr->size() )
            pColArr = nullptr;
    }

    UIName aTableName(rTableName);
    if (aTableName.isEmpty() || FindTableFormatByName(aTableName) != nullptr)
        aTableName = GetUniqueTableName();

    if( GetIDocumentUndoRedo().DoesUndo() )
    {
        GetIDocumentUndoRedo().AppendUndo(
            std::make_unique<SwUndoInsTable>( rPos, nCols, nRows, o3tl::narrowing<sal_uInt16>(eAdjust),
                                      rInsTableOpts, pTAFormat, pColArr,
                                      aTableName));
    }

    // Start with inserting the Nodes and get the AutoFormat for the Table
    SwTextFormatColl *pBodyColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ),
                 *pHeadColl = pBodyColl;

    bool bDfltBorders( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder );

    if( (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) && (1 != nRows || !bDfltBorders) )
        pHeadColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN );

    const sal_uInt16 nRowsToRepeat =
            SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
            rInsTableOpts.mnRowsToRepeat :
            0;

    /* Save content node to extract FRAMEDIR from. */
    const SwContentNode * pContentNd = rPos.GetNode().GetContentNode();

    /* If we are called from a shell pass the attrset from
        pContentNd (aka the node the table is inserted at) thus causing
        SwNodes::InsertTable to propagate an adjust item if
        necessary. */

    SwTableNode *pTableNd = SwNodes::InsertTable(
        rPos.GetNode(),
        nCols,
        pBodyColl,
        nRows,
        nRowsToRepeat,
        pHeadColl,
        bCalledFromShell ? &pContentNd->GetSwAttrSet() : nullptr );

    // Create the Box/Line/Table construct
    SwTableLineFormat* pLineFormat = MakeTableLineFormat();
    SwTableFormat* pTableFormat = MakeTableFrameFormat( aTableName, GetDfltFrameFormat() );

    /* If the node to insert the table at is a context node and has a
       non-default FRAMEDIR propagate it to the table. */

    if (pContentNd)
    {
        const SwAttrSet & aNdSet = pContentNd->GetSwAttrSet();
        if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
        {
            pTableFormat->SetFormatAttr( *pItem );
        }
    }

    // Set Orientation at the Table's Format
    pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
    // All lines use the left-to-right Fill-Order!
    pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));

    // Set USHRT_MAX as the Table's default SSize
    SwTwips nWidth = USHRT_MAX;
    if( pColArr )
    {
        sal_uInt16 nSttPos = pColArr->front();
        sal_uInt16 nLastPos = pColArr->back();
        if( text::HoriOrientation::NONE == eAdjust )
        {
            sal_uInt16 nFrameWidth = nLastPos;
            nLastPos = (*pColArr)[ pColArr->size()-2 ];
            pTableFormat->SetFormatAttr(SvxLRSpaceItem(
                SvxIndentValue::twips(nSttPos), SvxIndentValue::twips(nFrameWidth - nLastPos),
                SvxIndentValue::zero(), RES_LR_SPACE));
        }
        nWidth = nLastPos - nSttPos;
    }
    else
    {
        nWidth /= nCols;
        nWidth *= nCols; // to avoid rounding problems
    }
    pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth ));
    if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
        pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));

    // Move the hard PageDesc/PageBreak Attributes if needed
    SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]
                            ->GetContentNode();
    if( pNextNd && pNextNd->HasSwAttrSet() )
    {
        const SfxItemSet* pNdSet = pNextNd->GetpSwAttrSet();
        ifconst SwFormatPageDesc* pItem = pNdSet->GetItemIfSet( RES_PAGEDESC, false ) )
        {
            pTableFormat->SetFormatAttr( *pItem );
            pNextNd->ResetAttr( RES_PAGEDESC );
            pNdSet = pNextNd->GetpSwAttrSet();
        }
        const SvxFormatBreakItem* pItem;
        if( pNdSet && (pItem = pNdSet->GetItemIfSet( RES_BREAK, false )) )
        {
            pTableFormat->SetFormatAttr( *pItem );
            pNextNd->ResetAttr( RES_BREAK );
        }
    }

    SwTable& rNdTable = pTableNd->GetTable();
    rNdTable.RegisterToFormat( *pTableFormat );

    rNdTable.SetRowsToRepeat( nRowsToRepeat );
    rNdTable.SetTableModel( bNewModel );

    std::vector<SwTableBoxFormat*> aBoxFormatArr;
    SwTableBoxFormat* pBoxFormat = nullptr;
    if( !bDfltBorders && !pTAFormat )
    {
        pBoxFormat = MakeTableBoxFormat();
        pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX / nCols, 0 ));
    }
    else
    {
        const sal_uInt16 nBoxArrLen = pTAFormat ? 16 : 4;
        aBoxFormatArr.resize( nBoxArrLen, nullptr );
    }
    SfxItemSet aCharSet(SfxItemSet::makeFixedSfxItemSet<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1>(GetAttrPool()));

    SwNodeIndex aNdIdx( *pTableNd, 1 ); // Set to StartNode of first Box
    SwTableLines& rLines = rNdTable.GetTabLines();
    for( sal_uInt16 n = 0; n < nRows; ++n )
    {
        SwTableLine* pLine = new SwTableLine( pLineFormat, nCols, nullptr );
        rLines.insert( rLines.begin() + n, pLine );
        SwTableBoxes& rBoxes = pLine->GetTabBoxes();
        for( sal_uInt16 i = 0; i < nCols; ++i )
        {
            SwTableBoxFormat *pBoxF;
            if( pTAFormat )
            {
                sal_uInt8 nId = SwTableAutoFormat::CountPos(i, nCols, n, nRows);
                pBoxF = ::lcl_CreateAFormatBoxFormat( *this, aBoxFormatArr, *pTAFormat,
                                                nRows, nCols, nId );

                // Set the Paragraph/Character Attributes if needed
                if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
                {
                    aCharSet.ClearItem();
                    pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
                                        SwTableAutoFormatUpdateFlags::Char, nullptr );
                    if( aCharSet.Count() )
                        GetNodes()[ aNdIdx.GetIndex()+1 ]->GetContentNode()->
                            SetAttr( aCharSet );
                }
            }
            else if( bDfltBorders )
            {
                sal_uInt8 nBoxId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
                pBoxF = ::lcl_CreateDfltBoxFormat( *this, aBoxFormatArr, nCols, nBoxId);
            }
            else
                pBoxF = pBoxFormat;

            // For AutoFormat on input: the columns are set when inserting the Table
            // The Array contains the columns positions and not their widths!
            if( pColArr )
            {
                nWidth = (*pColArr)[ i + 1 ] - (*pColArr)[ i ];
                if( pBoxF->GetFrameSize().GetWidth() != nWidth )
                {
                    if( pBoxF->HasWriterListeners() ) // Create new Format
                    {
                        SwTableBoxFormat *pNewFormat = MakeTableBoxFormat();
                        *pNewFormat = *pBoxF;
                        pBoxF = pNewFormat;
                    }
                    pBoxF->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth ));
                }
            }

            SwTableBox *pBox = new SwTableBox( pBoxF, aNdIdx, pLine);
            rBoxes.insert( rBoxes.begin() + i, pBox );
            aNdIdx += SwNodeOffset(3); // StartNode, TextNode, EndNode  == 3 Nodes
        }
    }
    // Insert Frames
    pTableNd->MakeOwnFrames();

    // To-Do - add 'SwExtraRedlineTable' also ?
    if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
    {
        SwPaM aPam( *pTableNd->EndOfSectionNode(), *pTableNd, SwNodeOffset(1) );
        if( getIDocumentRedlineAccess().IsRedlineOn() )
            getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
        else
            getIDocumentRedlineAccess().SplitRedline( aPam );
    }

    getIDocumentState().SetModified();
    CHECK_TABLE(rNdTable);
    return &rNdTable;
}

SwTableNode* SwNodes::InsertTable( const SwNode& rNd,
                                   sal_uInt16 nBoxes,
                                   SwTextFormatColl* pContentTextColl,
                                   sal_uInt16 nLines,
                                   sal_uInt16 nRepeat,
                                   SwTextFormatColl* pHeadlineTextColl,
                                   const SwAttrSet * pAttrSet)
{
    if( !nBoxes )
        return nullptr;

    // If Lines is given, create the Matrix from Lines and Boxes
    if( !pHeadlineTextColl || !nLines )
        pHeadlineTextColl = pContentTextColl;

    SwTableNode * pTableNd = new SwTableNode( rNd );
    SwEndNode* pEndNd = new SwEndNode( rNd, *pTableNd );

    if( !nLines ) // For the for loop
        ++nLines;

    SwTextFormatColl* pTextColl = pHeadlineTextColl;
    for( sal_uInt16 nL = 0; nL < nLines; ++nL )
    {
        for( sal_uInt16 nB = 0; nB < nBoxes; ++nB )
        {
            SwStartNode* pSttNd = new SwStartNode( *pEndNd, SwNodeType::Start,
                                                    SwTableBoxStartNode );
            pSttNd->m_pStartOfSection = pTableNd;

            SwTextNode * pTmpNd = new SwTextNode( *pEndNd, pTextColl );

            // #i60422# Propagate some more attributes.
            const SfxPoolItem* pItem = nullptr;
            if ( nullptr != pAttrSet )
            {
                static const sal_uInt16 aPropagateItems[] = {
                    RES_PARATR_ADJUST,
                    RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
                    RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE,
                    RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, 0 };

                const sal_uInt16* pIdx = aPropagateItems;
                while ( *pIdx != 0 )
                {
                    if ( SfxItemState::SET != pTmpNd->GetSwAttrSet().GetItemState( *pIdx ) &&
                         SfxItemState::SET == pAttrSet->GetItemState( *pIdx, true, &pItem ) )
                        static_cast<SwContentNode *>(pTmpNd)->SetAttr(*pItem);
                    ++pIdx;
                }
            }

            new SwEndNode( *pEndNd, *pSttNd );
        }
        if ( nL + 1 >= nRepeat )
            pTextColl = pContentTextColl;
    }
    return pTableNd;
}

/**
 * Text to Table
 */

const SwTable* SwDoc::TextToTable( const SwInsertTableOptions& rInsTableOpts,
                                   const SwPaM& rRange, sal_Unicode cCh,
                                   sal_Int16 eAdjust,
                                   const SwTableAutoFormat* pTAFormat )
{
    // See if the selection contains a Table
    auto [pStart, pEnd] = rRange.StartEnd(); // SwPosition*
    {
        SwNodeOffset nCnt = pStart->GetNodeIndex();
        for( ; nCnt <= pEnd->GetNodeIndex(); ++nCnt )
            if( !GetNodes()[ nCnt ]->IsTextNode() )
                return nullptr;
    }

    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().StartUndo(SwUndoId::TEXTTOTABLE, nullptr);
    }

    // tdf#153115 first, remove all redlines; splitting them at cell boundaries
    // would be tricky to implement, and it's unclear what the value of
    // existing redlines is once it's been converted to a table
    getIDocumentRedlineAccess().AcceptRedline(rRange, true);

    // Save first node in the selection if it is a context node
    SwContentNode * pSttContentNd = pStart->GetNode().GetContentNode();

    SwPaM aOriginal( *pStart, *pEnd );
    pStart = aOriginal.GetMark();
    pEnd = aOriginal.GetPoint();

    SwUndoTextToTable* pUndo = nullptr;
    if( GetIDocumentUndoRedo().DoesUndo() )
    {
        pUndo = new SwUndoTextToTable( aOriginal, rInsTableOpts, cCh,
                    o3tl::narrowing<sal_uInt16>(eAdjust), pTAFormat );
        GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );

        // Do not add splitting the TextNode to the Undo history
        GetIDocumentUndoRedo().DoUndo( false );
    }

    ::PaMCorrAbs( aOriginal, *pEnd );

    // Make sure that the range is on Node Edges
    SwNodeRange aRg( pStart->GetNode(), pEnd->GetNode() );
    if( pStart->GetContentIndex() )
        getIDocumentContentOperations().SplitNode( *pStart, false );

    bool bEndContent = 0 != pEnd->GetContentIndex();

    // Do not split at the End of a Line (except at the End of the Doc)
    if( bEndContent )
    {
        if( pEnd->GetNode().GetContentNode()->Len() != pEnd->GetContentIndex()
            || pEnd->GetNodeIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
        {
            getIDocumentContentOperations().SplitNode( *pEnd, false );
            const_cast<SwPosition*>(pEnd)->Adjust(SwNodeOffset(-1));
            // A Node and at the End?
            if( pStart->GetNodeIndex() >= pEnd->GetNodeIndex() )
                --aRg.aStart;
        }
        else
            ++aRg.aEnd;
    }

    if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
    {
        OSL_FAIL( "empty range" );
        ++aRg.aEnd;
    }

    // We always use Upper to insert the Table
    SwNode2LayoutSaveUpperFrames aNode2Layout( aRg.aStart.GetNode() );

    GetIDocumentUndoRedo().DoUndo( nullptr != pUndo );

    // Create the Box/Line/Table construct
    SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
    SwTableLineFormat* pLineFormat = MakeTableLineFormat();
    SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );

    // All Lines have a left-to-right Fill Order
    pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
    // The Table's SSize is USHRT_MAX
    pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX ));
    if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
        pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));

    /* If the first node in the selection is a context node and if it
       has an item FRAMEDIR set (no default) propagate the item to the
       replacing table. */

    if (pSttContentNd)
    {
        const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
        if (const SvxFrameDirectionItem *pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ) )
        {
            pTableFormat->SetFormatAttr( *pItem );
        }
    }

    //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
    //until after RegisterToFormat is completed
    bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
    getIDocumentState().SetEnableSetModified(false);

    SwTableNode* pTableNd = GetNodes().TextToTable(
            aRg, cCh, pTableFormat, pLineFormat, pBoxFormat,
            getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ), pUndo );

    SwTable& rNdTable = pTableNd->GetTable();

    const sal_uInt16 nRowsToRepeat =
            SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
            rInsTableOpts.mnRowsToRepeat :
            0;
    rNdTable.SetRowsToRepeat(nRowsToRepeat);

    bool bUseBoxFormat = false;
    if( !pBoxFormat->HasWriterListeners() )
    {
        // The Box's Formats already have the right size, we must only set
        // the right Border/AutoFormat.
        bUseBoxFormat = true;
        pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
        delete pBoxFormat;
        eAdjust = text::HoriOrientation::NONE;
    }

    // Set Orientation in the Table's Format
    pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
    rNdTable.RegisterToFormat(*pTableFormat);

    if( pTAFormat || ( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder) )
    {
        sal_uInt8 nBoxArrLen = pTAFormat ? 16 : 4;
        std::unique_ptr< DfltBoxAttrList_t > aBoxFormatArr1;
        std::optional< std::vector<SwTableBoxFormat*> > aBoxFormatArr2;
        if( bUseBoxFormat )
        {
            aBoxFormatArr1.reset(new DfltBoxAttrList_t( nBoxArrLen, nullptr ));
        }
        else
        {
            aBoxFormatArr2 = std::vector<SwTableBoxFormat*>( nBoxArrLen, nullptr );
        }

        SfxItemSet aCharSet(SfxItemSet::makeFixedSfxItemSet<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1>(GetAttrPool()));

        SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;

        SwTableBoxFormat *pBoxF = nullptr;
        SwTableLines& rLines = rNdTable.GetTabLines();
        const SwTableLines::size_type nRows = rLines.size();
        for( SwTableLines::size_type n = 0; n < nRows; ++n )
        {
            SwTableBoxes& rBoxes = rLines[ n ]->GetTabBoxes();
            const SwTableBoxes::size_type nCols = rBoxes.size();
            for( SwTableBoxes::size_type i = 0; i < nCols; ++i )
            {
                SwTableBox* pBox = rBoxes[ i ];
                bool bChgSz = false;

                if( pTAFormat )
                {
                    sal_uInt8 nId = static_cast<sal_uInt8>(!n ? 0 : (( n+1 == nRows )
                                            ? 12 : (4 * (1 + ((n-1) & 1 )))));
                    nId = nId + static_cast<sal_uInt8>(!i ? 0 :
                                ( i+1 == nCols ? 3 : (1 + ((i-1) & 1))));
                    if( bUseBoxFormat )
                        ::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId, pTAFormat );
                    else
                    {
                        bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
                        pBoxF = ::lcl_CreateAFormatBoxFormat( *this, *aBoxFormatArr2,
                                                *pTAFormat, USHRT_MAX, USHRT_MAX, nId );
                    }

                    // Set Paragraph/Character Attributes if needed
                    if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
                    {
                        aCharSet.ClearItem();
                        pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
                                            SwTableAutoFormatUpdateFlags::Char, nullptr );
                        if( aCharSet.Count() )
                        {
                            SwNodeOffset nSttNd = pBox->GetSttIdx()+1;
                            SwNodeOffset nEndNd = pBox->GetSttNd()->EndOfSectionIndex();
                            for( ; nSttNd < nEndNd; ++nSttNd )
                            {
                                SwContentNode* pNd = GetNodes()[ nSttNd ]->GetContentNode();
                                if( pNd )
                                {
                                    if( pHistory )
                                    {
                                        SwRegHistory aReg( pNd, *pNd, pHistory );
                                        pNd->SetAttr( aCharSet );
                                    }
                                    else
                                        pNd->SetAttr( aCharSet );
                                }
                            }
                        }
                    }
                }
                else
                {
                    sal_uInt8 nId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
                    if( bUseBoxFormat )
                        ::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId );
                    else
                    {
                        bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
                        pBoxF = ::lcl_CreateDfltBoxFormat( *this, *aBoxFormatArr2,
                                                        USHRT_MAX, nId );
                    }
                }

                if( !bUseBoxFormat )
                {
                    if( bChgSz )
                        pBoxF->SetFormatAttr( pBox->GetFrameFormat()->GetFrameSize() );
                    pBox->ChgFrameFormat( pBoxF );
                }
            }
        }

        if( bUseBoxFormat )
        {
            for( sal_uInt8 i = 0; i < nBoxArrLen; ++i )
            {
                delete (*aBoxFormatArr1)[ i ];
            }
        }
    }

    // Check the boxes for numbers
    if( IsInsTableFormatNum() )
    {
        for (size_t nBoxes = rNdTable.GetTabSortBoxes().size(); nBoxes; )
        {
            ChkBoxNumFormat(*rNdTable.GetTabSortBoxes()[ --nBoxes ], false);
        }
    }

    SwNodeOffset nIdx = pTableNd->GetIndex();
    aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );

    {
        SwPaM& rTmp = const_cast<SwPaM&>(rRange); // Point always at the Start
        rTmp.DeleteMark();
        rTmp.GetPoint()->Assign( *pTableNd );
        SwNodes::GoNext(rTmp.GetPoint());
    }

    if( pUndo )
    {
        GetIDocumentUndoRedo().EndUndo( SwUndoId::TEXTTOTABLE, nullptr );
    }

    getIDocumentState().SetEnableSetModified(bEnableSetModified);
    getIDocumentState().SetModified();
    getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
    return &rNdTable;
}

static void lcl_RemoveBreaksTable(SwTableNode & rNode, SwTableFormat *const pTableFormat)
{
    // delete old layout frames, new ones need to be created...
    rNode.DelFrames(nullptr);

    // remove PageBreaks/PageDesc/ColBreak
    SwFrameFormat & rFormat(*rNode.GetTable().GetFrameFormat());

    if (const SvxFormatBreakItem* pItem = rFormat.GetItemIfSet(RES_BREAK, false))
    {
        if (pTableFormat)
        {
            pTableFormat->SetFormatAttr(*pItem);
        }
        rFormat.ResetFormatAttr(RES_BREAK);
    }

    SwFormatPageDesc const*const pPageDescItem(rFormat.GetItemIfSet(RES_PAGEDESC, false));
    if (pPageDescItem && pPageDescItem->GetPageDesc())
    {
        if (pTableFormat)
        {
            pTableFormat->SetFormatAttr(*pPageDescItem);
        }
        rFormat.ResetFormatAttr(RES_PAGEDESC);
    }
}

static void lcl_RemoveBreaks(SwContentNode & rNode, SwTableFormat *const pTableFormat)
{
    // delete old layout frames, new ones need to be created...
    rNode.DelFrames(nullptr);

    if (!rNode.IsTextNode())
    {
        return;
    }

    SwTextNode & rTextNode = *rNode.GetTextNode();
    // remove PageBreaks/PageDesc/ColBreak
    SfxItemSet const* pSet = rTextNode.GetpSwAttrSet();
    if (!pSet)
        return;

    if (const SvxFormatBreakItem* pItem = pSet->GetItemIfSet(RES_BREAK, false))
    {
        if (pTableFormat)
        {
            pTableFormat->SetFormatAttr(*pItem);
        }
        rTextNode.ResetAttr(RES_BREAK);
        pSet = rTextNode.GetpSwAttrSet();
    }

    const SwFormatPageDesc* pPageDescItem;
    if (pSet
        && (pPageDescItem = pSet->GetItemIfSet(RES_PAGEDESC, false))
        && pPageDescItem->GetPageDesc())
    {
        if (pTableFormat)
        {
            pTableFormat->SetFormatAttr(*pPageDescItem);
        }
        rTextNode.ResetAttr(RES_PAGEDESC);
    }
}

/**
 * balance lines in table, insert empty boxes so all lines have the size
 */

static void
lcl_BalanceTable(SwTable & rTable, size_t const nMaxBoxes,
    SwTableNode & rTableNd, SwTableBoxFormat & rBoxFormat, SwTextFormatColl & rTextColl,
    SwUndoTextToTable *const pUndo, std::vector<sal_uInt16> *const pPositions)
{
    for (size_t n = 0; n < rTable.GetTabLines().size(); ++n)
    {
        SwTableLine *const pCurrLine = rTable.GetTabLines()[ n ];
        size_t const nBoxes = pCurrLine->GetTabBoxes().size();
        if (nMaxBoxes != nBoxes)
        {
            rTableNd.GetNodes().InsBoxen(&rTableNd, pCurrLine, &rBoxFormat, &rTextColl,
                    nullptr, nBoxes, nMaxBoxes - nBoxes);

            if (pUndo)
            {
                for (size_t i = nBoxes; i < nMaxBoxes; ++i)
                {
                    pUndo->AddFillBox( *pCurrLine->GetTabBoxes()[i] );
                }
            }

            // if the first line is missing boxes, the width array is useless!
            if (!n && pPositions)
            {
                pPositions->clear();
            }
        }
    }
}

static void
lcl_SetTableBoxWidths(SwTable & rTable, size_t const nMaxBoxes,
        SwTableBoxFormat & rBoxFormat, SwDoc & rDoc,
        std::vector<sal_uInt16> *const pPositions)
{
    if (pPositions && !pPositions->empty())
    {
        SwTableLines& rLns = rTable.GetTabLines();
        sal_uInt16 nLastPos = 0;
        for (size_t n = 0; n < pPositions->size(); ++n)
        {
            SwTableBoxFormat *pNewFormat = rDoc.MakeTableBoxFormat();
            pNewFormat->SetFormatAttr(
                    SwFormatFrameSize(SwFrameSize::Variable, (*pPositions)[n] - nLastPos));
            for (size_t nTmpLine = 0; nTmpLine < rLns.size(); ++nTmpLine)
            {
                // Have to do an Add here, because the BoxFormat
                // is still needed by the caller
                pNewFormat->Add(*rLns[nTmpLine]->GetTabBoxes()[n]);
            }

            nLastPos = (*pPositions)[ n ];
        }

        // propagate size upwards from format, so the table gets the right size
        SAL_WARN_IF(rBoxFormat.HasWriterListeners(), "sw.core",
                "who is still registered in the format?");
        rBoxFormat.SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nLastPos ));
    }
    else
    {
        size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
        rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, nWidth));
    }
}

SwTableNode* SwNodes::TextToTable( const SwNodeRange& rRange, sal_Unicode cCh,
                                    SwTableFormat* pTableFormat,
                                    SwTableLineFormat* pLineFormat,
                                    SwTableBoxFormat* pBoxFormat,
                                    SwTextFormatColl* pTextColl,
                                    SwUndoTextToTable* pUndo )
{
    if( rRange.aStart >= rRange.aEnd )
        return nullptr;

    SwTableNode * pTableNd = new SwTableNode( rRange.aStart.GetNode() );
    new SwEndNode( rRange.aEnd.GetNode(), *pTableNd );

    SwDoc& rDoc = GetDoc();
    std::vector<sal_uInt16> aPosArr;
    SwTable& rTable = pTableNd->GetTable();
    SwTableBox* pBox;
    sal_uInt16 nBoxes, nLines, nMaxBoxes = 0;

    SwNodeIndex aSttIdx( *pTableNd, 1 );
    SwNodeIndex aEndIdx( rRange.aEnd, -1 );
    for( nLines = 0, nBoxes = 0;
        aSttIdx.GetIndex() < aEndIdx.GetIndex();
        aSttIdx += SwNodeOffset(2), nLines++, nBoxes = 0 )
    {
        SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode();
        OSL_ENSURE( pTextNd, "Only add TextNodes to the Table" );

        if( !nLines && 0x0b == cCh )
        {
            cCh = 0x09;

            // Get the separator's position from the first Node, in order for the Boxes to be set accordingly
            SwTextFrameInfo aFInfo( static_cast<SwTextFrame*>(pTextNd->getLayoutFrame( pTextNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )) );
            if( aFInfo.IsOneLine() ) // only makes sense in this case
            {
                OUString const& rText(pTextNd->GetText());
                for (sal_Int32 nChPos = 0; nChPos < rText.getLength(); ++nChPos)
                {
                    if (rText[nChPos] == cCh)
                    {
                        // sw_redlinehide: no idea if this makes any sense...
                        TextFrameIndex const nPos(aFInfo.GetFrame()->MapModelToView(pTextNd, nChPos));
                        aPosArr.push_back( o3tl::narrowing<sal_uInt16>(
                            aFInfo.GetCharPos(nPos+TextFrameIndex(1), false)) );
                    }
                }

                aPosArr.push_back(
                                o3tl::narrowing<sal_uInt16>(aFInfo.GetFrame()->IsVertical() ?
                                aFInfo.GetFrame()->getFramePrintArea().Bottom() :
                                aFInfo.GetFrame()->getFramePrintArea().Right()) );

            }
        }

        lcl_RemoveBreaks(*pTextNd, (0 == nLines) ? pTableFormat : nullptr);

        // Set the TableNode as StartNode for all TextNodes in the Table
        pTextNd->m_pStartOfSection = pTableNd;

        SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
        rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);

        SwStartNode* pSttNd;
        SwPosition aCntPos( aSttIdx, pTextNd, 0);

        const std::shared_ptr< sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
        pContentStore->Save(rDoc, aSttIdx.GetIndex(), SAL_MAX_INT32);

        if( T2T_PARA != cCh )
        {
            for (sal_Int32 nChPos = 0; nChPos < pTextNd->GetText().getLength();)
            {
                if (pTextNd->GetText()[nChPos] == cCh)
                {
                    aCntPos.SetContent(nChPos);
                    std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
                        [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode, bool)
                        {
                            if (!pContentStore->Empty())
                            {
                                pContentStore->Restore(*pNewNode, nChPos, nChPos + 1, eMode);
                            }
                        });
                    SwContentNode *const pNewNd =
                        pTextNd->SplitContentNode(aCntPos, &restoreFunc);

                    // Delete separator and correct search string
                    pTextNd->EraseText( aCntPos, 1 );
                    nChPos = 0;

                    // Set the TableNode as StartNode for all TextNodes in the Table
                    const SwNodeIndex aTmpIdx( aCntPos.GetNode(), -1 );
                    pSttNd = new SwStartNode( aTmpIdx.GetNode(), SwNodeType::Start,
                                                SwTableBoxStartNode );
                    new SwEndNode( aCntPos.GetNode(), *pSttNd );
                    pNewNd->m_pStartOfSection = pSttNd;

                    // Assign Section to the Box
                    pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
                    pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
                }
                else
                {
                    ++nChPos;
                }
            }
        }

        // Now for the last substring
        if( !pContentStore->Empty())
            pContentStore->Restore( *pTextNd, pTextNd->GetText().getLength(), pTextNd->GetText().getLength()+1 );

        pSttNd = new SwStartNode( aCntPos.GetNode(), SwNodeType::Start, SwTableBoxStartNode );
        const SwNodeIndex aTmpIdx( aCntPos.GetNode(), 1 );
        new SwEndNode( aTmpIdx.GetNode(), *pSttNd  );
        pTextNd->m_pStartOfSection = pSttNd;

        pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
        pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
        if( nMaxBoxes < nBoxes )
            nMaxBoxes = nBoxes;
    }

    lcl_BalanceTable(rTable, nMaxBoxes, *pTableNd, *pBoxFormat, *pTextColl,
            pUndo, &aPosArr);
    lcl_SetTableBoxWidths(rTable, nMaxBoxes, *pBoxFormat, rDoc, &aPosArr);

    return pTableNd;
}

const SwTable* SwDoc::TextToTable( const std::vector< std::vector<SwNodeRange> >& rTableNodes )
{
    if (rTableNodes.empty())
        return nullptr;

    const std::vector<SwNodeRange>& rFirstRange = *rTableNodes.begin();

    if (rFirstRange.empty())
        return nullptr;

    const std::vector<SwNodeRange>& rLastRange = *rTableNodes.rbegin();

    if (rLastRange.empty())
        return nullptr;

    /* Save first node in the selection if it is a content node. */
    SwContentNode * pSttContentNd = rFirstRange.begin()->aStart.GetNode().GetContentNode();

    const SwNodeRange& rStartRange = *rFirstRange.begin();
    const SwNodeRange& rEndRange = *rLastRange.rbegin();

    //!!! not necessarily TextNodes !!!
    SwPaM aOriginal( rStartRange.aStart, rEndRange.aEnd );
    const SwPosition *pStart = aOriginal.GetMark();
    SwPosition *pEnd = aOriginal.GetPoint();

    bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
    if (bUndo)
    {
        // Do not add splitting the TextNode to the Undo history
        GetIDocumentUndoRedo().DoUndo(false);
    }

    ::PaMCorrAbs( aOriginal, *pEnd );

    // make sure that the range is on Node Edges
    SwNodeRange aRg( pStart->GetNode(), pEnd->GetNode() );
    if( pStart->GetContentIndex() )
        getIDocumentContentOperations().SplitNode( *pStart, false );

    bool bEndContent = 0 != pEnd->GetContentIndex();

    // Do not split at the End of a Line (except at the End of the Doc)
    if( bEndContent )
    {
        if( pEnd->GetNode().GetContentNode()->Len() != pEnd->GetContentIndex()
            || pEnd->GetNodeIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
        {
            getIDocumentContentOperations().SplitNode( *pEnd, false );
            pEnd->Adjust(SwNodeOffset(-1));
            // A Node and at the End?
            if( pStart->GetNodeIndex() >= pEnd->GetNodeIndex() )
                --aRg.aStart;
        }
        else
            ++aRg.aEnd;
    }

    assert(aRg.aEnd.GetNode() == pEnd->GetNode());
    assert(aRg.aStart.GetNode() == pStart->GetNode());
    if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
    {
        OSL_FAIL( "empty range" );
        ++aRg.aEnd;
    }


    {
        // TODO: this is not Undo-able - only good enough for file import
        IDocumentRedlineAccess & rIDRA(getIDocumentRedlineAccess());
        SwNodeIndex const prev(rTableNodes.begin()->begin()->aStart, -1);
        SwNodeIndex const* pPrev(&prev);
        // pPrev could point to non-textnode now
        for (const auto& rRow : rTableNodes)
        {
            for (const auto& rCell : rRow)
            {
                assert(SwNodeIndex(*pPrev, +1) == rCell.aStart);
                SwPaM pam(rCell.aStart, 0, *pPrev,
                        (pPrev->GetNode().IsContentNode())
                            ? pPrev->GetNode().GetContentNode()->Len() : 0);
                rIDRA.SplitRedline(pam);
                pPrev = &rCell.aEnd;
            }
        }
        // another one to break between last cell and node after table
        SwPaM pam(pPrev->GetNode(), SwNodeOffset(+1), 0,
                  pPrev->GetNode(), SwNodeOffset(0),
                    (pPrev->GetNode().IsContentNode())
                        ? pPrev->GetNode().GetContentNode()->Len() : 0);
        rIDRA.SplitRedline(pam);

        // Paragraph formatting results in overlapping elements, split of redlines then can result
        // in an unsorted redline table, fix it up.
        SwRedlineTable& rRedlineTable = rIDRA.GetRedlineTable();
        if (rRedlineTable.HasOverlappingElements())
        {
            rIDRA.GetRedlineTable().Resort();
        }
    }

    // We always use Upper to insert the Table
    SwNode2LayoutSaveUpperFrames aNode2Layout( aRg.aStart.GetNode() );

    GetIDocumentUndoRedo().DoUndo(bUndo);

    // Create the Box/Line/Table construct
    SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
    SwTableLineFormat* pLineFormat = MakeTableLineFormat();
    SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );

    // All Lines have a left-to-right Fill Order
    pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
    // The Table's SSize is USHRT_MAX
    pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX ));

    /* If the first node in the selection is a context node and if it
       has an item FRAMEDIR set (no default) propagate the item to the
       replacing table. */

    if (pSttContentNd)
    {
        const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
        if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
        {
            pTableFormat->SetFormatAttr( *pItem );
        }
    }

    //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
    //until after RegisterToFormat is completed
    bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
    getIDocumentState().SetEnableSetModified(false);

    SwTableNode* pTableNd = GetNodes().TextToTable(
            rTableNodes, pTableFormat, pLineFormat, pBoxFormat );

    SwTable& rNdTable = pTableNd->GetTable();
    rNdTable.RegisterToFormat(*pTableFormat);

    if( !pBoxFormat->HasWriterListeners() )
    {
        // The Box's Formats already have the right size, we must only set
        // the right Border/AutoFormat.
        pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
        delete pBoxFormat;
    }

    SwNodeOffset nIdx = pTableNd->GetIndex();
    aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );

    getIDocumentState().SetEnableSetModified(bEnableSetModified);
    getIDocumentState().SetModified();
    getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
    return &rNdTable;
}

void SwNodes::ExpandRangeForTableBox(const SwNodeRange & rRange, std::optional<SwNodeRange>& rExpandedRange)
{
    bool bChanged = false;

    SwNodeIndex aNewStart = rRange.aStart;
    SwNodeIndex aNewEnd = rRange.aEnd;

    SwNodeIndex aEndIndex = rRange.aEnd;
    SwNodeIndex aIndex = rRange.aStart;

    while (aIndex < aEndIndex)
    {
        SwNode& rNode = aIndex.GetNode();

        if (rNode.IsStartNode())
        {
            // advance aIndex to the end node of this start node
            SwNode * pEndNode = rNode.EndOfSectionNode();
            aIndex = *pEndNode;

            if (aIndex > aNewEnd)
            {
                aNewEnd = aIndex;
                bChanged = true;
            }
        }
        else if (rNode.IsEndNode())
        {
            SwNode * pStartNode = rNode.StartOfSectionNode();
            if (pStartNode->GetIndex() < aNewStart.GetIndex())
            {
                aNewStart = *pStartNode;
                bChanged = true;
            }
        }

        if (aIndex < aEndIndex)
            ++aIndex;
    }

    SwNode * pNode = &aIndex.GetNode();
    while (pNode->IsEndNode() && aIndex < Count() - 1)
    {
        SwNode * pStartNode = pNode->StartOfSectionNode();
        aNewStart = *pStartNode;
        aNewEnd = aIndex;
        bChanged = true;

        ++aIndex;
        pNode = &aIndex.GetNode();
    }

    if (bChanged)
        rExpandedRange.emplace(aNewStart, aNewEnd);
}

static void
lcl_SetTableBoxWidths2(SwTable & rTable, size_t const nMaxBoxes,
        SwTableBoxFormat & rBoxFormat, SwDoc & rDoc)
{
    // rhbz#820283, fdo#55462: set default box widths so table width is covered
    SwTableLines & rLines = rTable.GetTabLines();
    assert(nMaxBoxes != 0); // no valid table without boxes
    auto const nTableWidth{USHRT_MAX}; // default
    auto const nWidth{nTableWidth / nMaxBoxes};
    auto const nRest{nTableWidth % nMaxBoxes};
    for (size_t nTmpLine = 0; nTmpLine < rLines.size(); ++nTmpLine)
    {
        SwTableBoxes & rBoxes = rLines[nTmpLine]->GetTabBoxes();
        assert(!rBoxes.empty()); // ensured by convertToTable
        size_t const nMissing = nMaxBoxes - rBoxes.size();
        if (nMissing || nRest != 0)
        {
            // default width for box at the end of an incomplete line
            SwTableBoxFormat *const pNewFormat = rDoc.MakeTableBoxFormat();
            pNewFormat->SetFormatAttr( SwFormatFrameSize(SwFrameSize::Variable,
                        nWidth * (nMissing + 1) + nRest) );
            pNewFormat->Add(*rBoxes.back());
        }
    }
    // default width for all boxes not at the end of an incomplete line
    rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, nWidth));
}

SwTableNode* SwNodes::TextToTable( const SwNodes::TableRanges_t & rTableNodes,
                                    SwTableFormat* pTableFormat,
                                    SwTableLineFormat* pLineFormat,
                                    SwTableBoxFormat* pBoxFormat  )
{
    if( rTableNodes.empty() )
        return nullptr;

    SwTableNode * pTableNd = new SwTableNode( rTableNodes.begin()->begin()->aStart.GetNode() );
    //insert the end node after the last text node
    SwNodeIndex aInsertIndex( rTableNodes.rbegin()->rbegin()->aEnd );
    ++aInsertIndex;

    //!! ownership will be transferred in c-tor to SwNodes array.
    //!! Thus no real problem here...
    new SwEndNode( aInsertIndex.GetNode(), *pTableNd );

    SwDoc& rDoc = GetDoc();
    SwTable& rTable = pTableNd->GetTable();
    SwTableBox* pBox;
    sal_uInt16 nLines, nMaxBoxes = 0;

    SwNodeIndex aNodeIndex = rTableNodes.begin()->begin()->aStart;
    // delete frames of all contained content nodes
    for( nLines = 0; aNodeIndex <= rTableNodes.rbegin()->rbegin()->aEnd; ++aNodeIndex,++nLines )
    {
        SwNode* pNode(&aNodeIndex.GetNode());
        while (pNode->IsSectionNode()) // could be ToX field in table
        {
            pNode = pNode->GetNodes()[pNode->GetIndex()+1];
        }
        if (pNode->IsTableNode())
        {
            lcl_RemoveBreaksTable(static_cast<SwTableNode&>(*pNode),
                    (0 == nLines) ? pTableFormat : nullptr);
        }
        else if (pNode->IsContentNode())
        {
            lcl_RemoveBreaks(static_cast<SwContentNode&>(*pNode),
                    (0 == nLines) ? pTableFormat : nullptr);
        }
    }

    nLines = 0;
    forconst auto& rRow : rTableNodes )
    {
        sal_uInt16 nBoxes = 0;
        SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
        rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);

        forconst auto& rCell : rRow )
        {
            SwNodeIndex aCellEndIdx(rCell.aEnd);
            ++aCellEndIdx;
            SwStartNode* pSttNd = new SwStartNode( rCell.aStart.GetNode(), SwNodeType::Start,
                                        SwTableBoxStartNode );

            // Quotation of http://nabble.documentfoundation.org/Some-strange-lines-by-taking-a-look-at-the-bt-of-fdo-51916-tp3994561p3994639.html
            // SwNode's constructor adds itself to the same SwNodes array as the other node (pSttNd).
            // So this statement is only executed for the side-effect.
            new SwEndNode( aCellEndIdx.GetNode(), *pSttNd );

            //set the start node on all node of the current cell
            SwNodeIndex aCellNodeIdx = rCell.aStart;
            for(;aCellNodeIdx <= rCell.aEnd; ++aCellNodeIdx )
            {
                aCellNodeIdx.GetNode().m_pStartOfSection = pSttNd;
                //skip start/end node pairs
                if( aCellNodeIdx.GetNode().IsStartNode() )
                    aCellNodeIdx.Assign(*aCellNodeIdx.GetNode().EndOfSectionNode());
            }

            // assign Section to the Box
            pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
            pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
        }
        if( nMaxBoxes < nBoxes )
            nMaxBoxes = nBoxes;

        nLines++;
    }

    lcl_SetTableBoxWidths2(rTable, nMaxBoxes, *pBoxFormat, rDoc);

    return pTableNd;
}

/**
 * Table to Text
 */

bool SwDoc::TableToText( const SwTableNode* pTableNd, sal_Unicode cCh )
{
    if( !pTableNd )
        return false;

    // #i34471#
    // If this is triggered by SwUndoTableToText::Repeat() nobody ever deleted
    // the table cursor.
    SwEditShell* pESh = GetEditShell();
    if (pESh && pESh->IsTableMode())
        pESh->ClearMark();

    SwNodeRange aRg( *pTableNd, SwNodeOffset(0), *pTableNd->EndOfSectionNode() );
    std::unique_ptr<SwUndoTableToText> pUndo;
    SwNodeRange* pUndoRg = nullptr;
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().ClearRedo();
        pUndoRg = new SwNodeRange( aRg.aStart, SwNodeOffset(-1), aRg.aEnd, SwNodeOffset(+1) );
        pUndo.reset(new SwUndoTableToText( pTableNd->GetTable(), cCh ));
    }

    const_cast<SwTable*>(&pTableNd->GetTable())->SwitchFormulasToExternalRepresentation();

    bool bRet = GetNodes().TableToText( aRg, cCh, pUndo.get() );
    if( pUndoRg )
    {
        ++pUndoRg->aStart;
        --pUndoRg->aEnd;
        pUndo->SetRange( *pUndoRg );
        GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
        delete pUndoRg;
    }

    if( bRet )
        getIDocumentState().SetModified();

    return bRet;
}

namespace {

/**
 * Use the ForEach method from PtrArray to recreate Text from a Table.
 * The Boxes can also contain Lines!
 */

struct DelTabPara
{
    SwTextNode* pLastNd;
    SwNodes& rNds;
    SwUndoTableToText* pUndo;
    sal_Unicode cCh;

    DelTabPara( SwNodes& rNodes, sal_Unicode cChar, SwUndoTableToText* pU ) :
        pLastNd(nullptr), rNds( rNodes ), pUndo( pU ), cCh( cChar ) {}
};

}

// Forward declare so that the Lines and Boxes can use recursion
static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara );

static void lcl_DelLine( SwTableLine* pLine, DelTabPara* pPara )
{
    assert(pPara && "The parameters are missing!");
    DelTabPara aPara( *pPara );
    forauto& rpBox : pLine->GetTabBoxes() )
        lcl_DelBox(rpBox, &aPara );
    if( pLine->GetUpper() ) // Is there a parent Box?
        // Return the last TextNode
        pPara->pLastNd = aPara.pLastNd;
}

static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara )
{
    assert(pDelPara && "The parameters are missing");

    // Delete the Box's Lines
    if( !pBox->GetTabLines().empty() )
    {
        for( SwTableLine* pLine : pBox->GetTabLines() )
            lcl_DelLine( pLine, pDelPara );
    }
    else
    {
        SwDoc& rDoc = pDelPara->rNds.GetDoc();
        SwNodeRange aDelRg( *pBox->GetSttNd(), SwNodeOffset(0),
                            *pBox->GetSttNd()->EndOfSectionNode() );
        // Delete the Section
        pDelPara->rNds.SectionUp( &aDelRg );
        const SwTextNode* pCurTextNd = nullptr;
        if (T2T_PARA != pDelPara->cCh && pDelPara->pLastNd)
            pCurTextNd = aDelRg.aStart.GetNode().GetTextNode();
        if (nullptr != pCurTextNd)
        {
            // Join the current text node with the last from the previous box if possible
            SwNodeOffset nNdIdx = aDelRg.aStart.GetIndex();
            --aDelRg.aStart;
            if( pDelPara->pLastNd == &aDelRg.aStart.GetNode() )
            {
                // Inserting the separator
                SwContentIndex aCntIdx( pDelPara->pLastNd,
                                 pDelPara->pLastNd->GetText().getLength());
                pDelPara->pLastNd->InsertText( OUString(pDelPara->cCh), aCntIdx,
                    SwInsertFlags::EMPTYEXPAND );
                if( pDelPara->pUndo )
                    pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex(),
                                                aCntIdx.GetIndex() );

                const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
                const sal_Int32 nOldTextLen = aCntIdx.GetIndex();
                pContentStore->Save(rDoc, nNdIdx, SAL_MAX_INT32);

                pDelPara->pLastNd->JoinNext();

                if( !pContentStore->Empty() )
                    pContentStore->Restore( rDoc, pDelPara->pLastNd->GetIndex(), nOldTextLen );
            }
            else if( pDelPara->pUndo )
            {
                ++aDelRg.aStart;
                pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex() );
            }
        }
        else if( pDelPara->pUndo )
            pDelPara->pUndo->AddBoxPos( rDoc, aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
        --aDelRg.aEnd;
        pDelPara->pLastNd = aDelRg.aEnd.GetNode().GetTextNode();

        // Do not take over the NumberFormatting's adjustment
        if( pDelPara->pLastNd && pDelPara->pLastNd->HasSwAttrSet() )
            pDelPara->pLastNd->ResetAttr( RES_PARATR_ADJUST );
    }
}

bool SwNodes::TableToText( const SwNodeRange& rRange, sal_Unicode cCh,
                            SwUndoTableToText* pUndo )
{
    // Is a Table selected?
    if (rRange.aStart.GetIndex() >= rRange.aEnd.GetIndex())
        return false;
    SwTableNode *const pTableNd(rRange.aStart.GetNode().GetTableNode());
    if (nullptr == pTableNd ||
        &rRange.aEnd.GetNode() != pTableNd->EndOfSectionNode() )
        return false;

    // If the Table was alone in a Section, create the Frames via the Table's Upper
    std::optional<SwNode2LayoutSaveUpperFrames> oNode2Layout;
    SwNode* pFrameNd = FindPrvNxtFrameNode( rRange.aStart.GetNode(), &rRange.aEnd.GetNode() );
    SwNodeIndex aFrameIdx( pFrameNd ? *pFrameNd: rRange.aStart.GetNode() );
    if( !pFrameNd )
        // Collect all Uppers
        oNode2Layout.emplace(*pTableNd);

    // Delete the Frames
    pTableNd->DelFrames();

    // "Delete" the Table and merge all Lines/Boxes
    DelTabPara aDelPara( *this, cCh, pUndo );
    for( SwTableLine *pLine : pTableNd->m_pTable->GetTabLines() )
        lcl_DelLine( pLine, &aDelPara );

    // We just created a TextNode with fitting separator for every TableLine.
    // Now we only need to delete the TableSection and create the Frames for the
    // new TextNode.
    SwNodeRange aDelRg( rRange.aStart, rRange.aEnd );

    // If the Table has PageDesc/Break Attributes, carry them over to the
    // first Text Node
    {
        // What about UNDO?
        const SfxItemSet& rTableSet = pTableNd->m_pTable->GetFrameFormat()->GetAttrSet();
        const SvxFormatBreakItem* pBreak = rTableSet.GetItemIfSet( RES_BREAK, false );
        const SwFormatPageDesc* pDesc = rTableSet.GetItemIfSet( RES_PAGEDESC, false );

        if( pBreak || pDesc )
        {
            SwNodeIndex aIdx( *pTableNd  );
            SwContentNode* pCNd = GoNext( &aIdx );
            if( pBreak )
                pCNd->SetAttr( *pBreak );
            if( pDesc )
                pCNd->SetAttr( *pDesc );
        }
    }

    SectionUp( &aDelRg ); // Delete this Section and by that the Table
    // #i28006#
    SwNodeOffset nStt = aDelRg.aStart.GetIndex(), nEnd = aDelRg.aEnd.GetIndex();
    if( !pFrameNd )
    {
        oNode2Layout->RestoreUpperFrames( *this,
                        aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
        oNode2Layout.reset();
    }
    else
    {
        SwContentNode *pCNd;
        SwSectionNode *pSNd;
        while( aDelRg.aStart.GetIndex() < nEnd )
        {
            pCNd = aDelRg.aStart.GetNode().GetContentNode();
            if( nullptr != pCNd )
            {
                if( pFrameNd->IsContentNode() )
                    static_cast<SwContentNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(*pCNd);
                else if( pFrameNd->IsTableNode() )
                    static_cast<SwTableNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
                else if( pFrameNd->IsSectionNode() )
                    static_cast<SwSectionNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
                pFrameNd = pCNd;
            }
            else
            {
                pSNd = aDelRg.aStart.GetNode().GetSectionNode();
                if( pSNd )
                {
                    if( !pSNd->GetSection().IsHidden() && !pSNd->IsContentHidden() )
                    {
                        pSNd->MakeOwnFrames(&aFrameIdx, &aDelRg.aEnd);
                        break;
                    }
                    aDelRg.aStart = *pSNd->EndOfSectionNode();
                }
            }
            ++aDelRg.aStart;
        }
    }

    // #i28006# Fly frames have to be restored even if the table was
    // #alone in the section
    for(sw::SpzFrameFormat* pFly: *GetDoc().GetSpzFrameFormats())
    {
        SwFrameFormat *const pFormat = pFly;
        const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
        SwNode const*const pAnchorNode = rAnchor.GetAnchorNode();
        if (pAnchorNode &&
            ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
             (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) &&
            nStt <= pAnchorNode->GetIndex() &&
            pAnchorNode->GetIndex() < nEnd )
        {
            pFormat->MakeFrames();
        }
    }

    return true;
}

/**
 * Inserting Columns/Rows
 */

void SwDoc::InsertCol( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
{
    if( !::CheckSplitCells( rCursor, nCnt + 1, SwTableSearchType::Col ) )
        return;

    // Find the Boxes via the Layout
    SwSelBoxes aBoxes;
    ::GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );

    if( !aBoxes.empty() )
        InsertCol( aBoxes, nCnt, bBehind );
}

bool SwDoc::InsertCol( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind, bool bInsertDummy )
{
    OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=88 G=89

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.