officecfg/registry/schema/org/openoffice/Office/Writer.xcs |    6 
 sw/inc/doc.hxx                                             |    1 
 sw/source/core/doc/doc.cxx                                 |  128 +++++++------
 3 files changed, 83 insertions(+), 52 deletions(-)

New commits:
commit 5555562856a8bca0869a04147fbc05a1eece9193
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Sat Oct 21 17:42:56 2023 +0300
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Sat Oct 21 21:48:34 2023 +0200

    Related: tdf#89178 Add an option to avoid converting some fields into text
    
    ... during mail merge.
    
    In some modes (generating individual documents; creating PDF) the mail merge
    process converts all fields into text. But sometimes it is undesirable for
    fields not involved into mail merge itself:
    
    * It is inconsistent with how MS Word behaves;
    * The generated editable documents could benefit from having other fields
      kept as fields;
    * Some fields, when exported to PDF, produce different results: e.g.,
      placeholder fields are output as empty spaces, not as placeholder text.
    
    An expert boolean configuration option is added:
    Office/Writer/FormLetter/ConvertToTextOnlyMMFields; it is false by default,
    in which case, the behavior is unchanged. When true, all fields in the mail
    merge document, except for database fields and hidden text fields, are not
    converted to text during mail merge process.
    
    Change-Id: Ibdb505ed3f2762db063bb0a91b674d27ecbc2e7f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158306
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/officecfg/registry/schema/org/openoffice/Office/Writer.xcs 
b/officecfg/registry/schema/org/openoffice/Office/Writer.xcs
index 75a019c63c73..c9da17b65c9e 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Writer.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Writer.xcs
@@ -6014,6 +6014,12 @@
           </prop>
         </group>
       </group>
+      <prop oor:name="ConvertToTextOnlyMMFields" oor:type="xs:boolean" 
oor:nillable="false">
+        <info>
+          <desc>When true, only fields that can be used in mail merge will be 
converted to text; all other fields will be kept as fields in the Mail Merge 
output</desc>
+        </info>
+        <value>false</value>
+      </prop>
     </group>
     <group oor:name="Misc">
       <info>
diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx
index c40abf7a1581..5d1814210801 100644
--- a/sw/inc/doc.hxx
+++ b/sw/inc/doc.hxx
@@ -1445,6 +1445,7 @@ public:
     // restore the invisible content if it's available on the undo stack
     bool RestoreInvisibleContent();
 
+    // Replace fields by text - mailmerge support
     SAL_DLLPRIVATE bool ConvertFieldsToText(SwRootFrame const& rLayout);
 
     // Create sub-documents according to given collection.
diff --git a/sw/source/core/doc/doc.cxx b/sw/source/core/doc/doc.cxx
index da14a84674e3..4bcb2a35e856 100644
--- a/sw/source/core/doc/doc.cxx
+++ b/sw/source/core/doc/doc.cxx
@@ -53,6 +53,8 @@
 #include <editeng/pbinitem.hxx>
 #include <unotools/localedatawrapper.hxx>
 
+#include <officecfg/Office/Writer.hxx>
+
 #include <swatrset.hxx>
 #include <swmodule.hxx>
 #include <fmtrfmrk.hxx>
@@ -1618,12 +1620,32 @@ bool SwDoc::RestoreInvisibleContent()
     return false;
 }
 
+static bool IsMailMergeField(SwFieldIds fieldId)
+{
+    switch (fieldId)
+    {
+        case SwFieldIds::Database: // Mail merge fields
+        case SwFieldIds::DatabaseName: // Database name
+        case SwFieldIds::HiddenText: // Hidden text may use database fields in 
condition
+        case SwFieldIds::HiddenPara: // Hidden paragraph may use database 
fields in condition
+        case SwFieldIds::DbNextSet: // Moving to next mail merge record
+        case SwFieldIds::DbNumSet: // Moving to a specific mail merge record
+        case SwFieldIds::DbSetNumber: // Number of current mail merge record
+            return true;
+        default:
+            return false;
+    }
+}
+
 bool SwDoc::ConvertFieldsToText(SwRootFrame const& rLayout)
 {
     bool bRet = false;
     getIDocumentFieldsAccess().LockExpFields();
     GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_REPLACE, nullptr );
 
+    const bool bOnlyConvertDBFields
+        = 
officecfg::Office::Writer::FormLetter::ConvertToTextOnlyMMFields::get();
+
     const SwFieldTypes* pMyFieldTypes = 
getIDocumentFieldsAccess().GetFieldTypes();
     const SwFieldTypes::size_type nCount = pMyFieldTypes->size();
     //go backward, field types are removed
@@ -1634,6 +1656,9 @@ bool SwDoc::ConvertFieldsToText(SwRootFrame const& 
rLayout)
         if ( SwFieldIds::Postit == pCurType->Which() )
             continue;
 
+        if (bOnlyConvertDBFields && !IsMailMergeField(pCurType->Which()))
+            continue;
+
         std::vector<SwFormatField*> vFieldFormats;
         pCurType->GatherFields(vFieldFormats, false);
         for(const auto& rpFieldFormat : vFieldFormats)
@@ -1644,67 +1669,66 @@ bool SwDoc::ConvertFieldsToText(SwRootFrame const& 
rLayout)
 
             bool bSkip = !pTextField ||
                          !pTextField->GetpTextNode()->GetNodes().IsDocNodes();
+            if (bSkip)
+                continue;
 
-            if (!bSkip)
+            bool bInHeaderFooter = 
IsInHeaderFooter(*pTextField->GetpTextNode());
+            const SwFormatField& rFormatField = pTextField->GetFormatField();
+            const SwField*  pField = rFormatField.GetField();
+
+            //#i55595# some fields have to be excluded in headers/footers
+            SwFieldIds nWhich = pField->GetTyp()->Which();
+            if(!bInHeaderFooter ||
+                    (nWhich != SwFieldIds::PageNumber &&
+                    nWhich != SwFieldIds::Chapter &&
+                    nWhich != SwFieldIds::GetExp&&
+                    nWhich != SwFieldIds::SetExp&&
+                    nWhich != SwFieldIds::Input&&
+                    nWhich != SwFieldIds::RefPageGet&&
+                    nWhich != SwFieldIds::RefPageSet))
             {
-                bool bInHeaderFooter = 
IsInHeaderFooter(*pTextField->GetpTextNode());
-                const SwFormatField& rFormatField = 
pTextField->GetFormatField();
-                const SwField*  pField = rFormatField.GetField();
-
-                //#i55595# some fields have to be excluded in headers/footers
-                SwFieldIds nWhich = pField->GetTyp()->Which();
-                if(!bInHeaderFooter ||
-                        (nWhich != SwFieldIds::PageNumber &&
-                        nWhich != SwFieldIds::Chapter &&
-                        nWhich != SwFieldIds::GetExp&&
-                        nWhich != SwFieldIds::SetExp&&
-                        nWhich != SwFieldIds::Input&&
-                        nWhich != SwFieldIds::RefPageGet&&
-                        nWhich != SwFieldIds::RefPageSet))
-                {
-                    OUString sText = pField->ExpandField(true, &rLayout);
+                OUString sText = pField->ExpandField(true, &rLayout);
 
-                    // database fields should not convert their command into 
text
-                    if( SwFieldIds::Database == pCurType->Which() && 
!static_cast<const SwDBField*>(pField)->IsInitialized())
-                        sText.clear();
+                // database fields should not convert their command into text
+                if( SwFieldIds::Database == pCurType->Which() && 
!static_cast<const SwDBField*>(pField)->IsInitialized())
+                    sText.clear();
 
-                    SwPaM aInsertPam(*pTextField->GetpTextNode(), 
pTextField->GetStart());
-                    aInsertPam.SetMark();
+                SwPaM aInsertPam(*pTextField->GetpTextNode(), 
pTextField->GetStart());
+                aInsertPam.SetMark();
 
-                    // go to the end of the field
-                    const SwTextField *pFieldAtEnd = 
sw::DocumentFieldsManager::GetTextFieldAtPos(*aInsertPam.End());
-                    if (pFieldAtEnd && pFieldAtEnd->Which() == 
RES_TXTATR_INPUTFIELD)
-                    {
-                        SwPosition &rEndPos = *aInsertPam.GetPoint();
-                        rEndPos.SetContent( 
SwCursorShell::EndOfInputFieldAtPos( *aInsertPam.End() ) );
-                    }
-                    else
-                    {
-                        aInsertPam.Move();
-                    }
-
-                    // first insert the text after field to keep the field's 
attributes,
-                    // then delete the field
-                    if (!sText.isEmpty())
-                    {
-                        // to keep the position after insert
-                        SwPaM aDelPam( *aInsertPam.GetMark(), 
*aInsertPam.GetPoint() );
-                        aDelPam.Move( fnMoveBackward );
-                        aInsertPam.DeleteMark();
+                // go to the end of the field
+                const SwTextField *pFieldAtEnd = 
sw::DocumentFieldsManager::GetTextFieldAtPos(*aInsertPam.End());
+                if (pFieldAtEnd && pFieldAtEnd->Which() == 
RES_TXTATR_INPUTFIELD)
+                {
+                    SwPosition &rEndPos = *aInsertPam.GetPoint();
+                    rEndPos.SetContent( SwCursorShell::EndOfInputFieldAtPos( 
*aInsertPam.End() ) );
+                }
+                else
+                {
+                    aInsertPam.Move();
+                }
 
-                        getIDocumentContentOperations().InsertString( 
aInsertPam, sText );
+                // first insert the text after field to keep the field's 
attributes,
+                // then delete the field
+                if (!sText.isEmpty())
+                {
+                    // to keep the position after insert
+                    SwPaM aDelPam( *aInsertPam.GetMark(), 
*aInsertPam.GetPoint() );
+                    aDelPam.Move( fnMoveBackward );
+                    aInsertPam.DeleteMark();
 
-                        aDelPam.Move();
-                        // finally remove the field
-                        getIDocumentContentOperations().DeleteAndJoin( aDelPam 
);
-                    }
-                    else
-                    {
-                        getIDocumentContentOperations().DeleteAndJoin( 
aInsertPam );
-                    }
+                    getIDocumentContentOperations().InsertString( aInsertPam, 
sText );
 
-                    bRet = true;
+                    aDelPam.Move();
+                    // finally remove the field
+                    getIDocumentContentOperations().DeleteAndJoin( aDelPam );
                 }
+                else
+                {
+                    getIDocumentContentOperations().DeleteAndJoin( aInsertPam 
);
+                }
+
+                bRet = true;
             }
         }
     }

Reply via email to