diff --git a/scribus/pageitem.cpp b/scribus/pageitem.cpp
index 22c5fcb3d3..6050021f8f 100644
--- a/scribus/pageitem.cpp
+++ b/scribus/pageitem.cpp
@@ -9881,6 +9881,29 @@ PageItem* PageItem::lastInChain()
 	return last;
 }
 
+bool PageItem::isAllNextInChainSamePage()
+{
+    PageItem* next = this;
+	while (next->nextInChain() != nullptr) {
+        next = next->nextInChain();
+        if (next->OwnPage != this->OwnPage) {
+            return false;
+        }
+    }
+	return true;
+}
+
+PageItem* PageItem::lastInChainSamePage()
+{
+	assert(isInChain());
+	auto last = lastInChain();
+	while (last->OwnPage != OwnPage && last->prevInChain() != nullptr) {
+		last = last->nextInChain();
+	}
+	return last;
+}
+
+
 QRect PageItem::getRedrawBounding(const double viewScale)
 {
 	int x = qRound(floor(BoundingX - Oldm_lineWidth / 2.0 - 5) * viewScale);
diff --git a/scribus/pageitem.h b/scribus/pageitem.h
index 31061f0aaa..162f476e31 100644
--- a/scribus/pageitem.h
+++ b/scribus/pageitem.h
@@ -465,6 +465,9 @@ class SCRIBUS_API PageItem : public QObject, public UndoObject, public SaxIO, pu
 	//you can change all code for search first or last item in chain
 	PageItem* firstInChain();
 	PageItem* lastInChain();
+	PageItem* lastInChainSamePage();
+	bool isAllNextInChainSamePage();
+
 	bool testLinkCandidate(PageItem* nextFrame);
 	void unlink(bool createUndo = true);
 	void link(PageItem* nextFrame, bool addPARSEP = true);
diff --git a/scribus/pageitem_textframe.cpp b/scribus/pageitem_textframe.cpp
index 9f5ec4b64a..dcd75df54c 100644
--- a/scribus/pageitem_textframe.cpp
+++ b/scribus/pageitem_textframe.cpp
@@ -1753,7 +1753,7 @@ void PageItem_TextFrame::layout()
 						realCharHeight = qMax(realCharHeight, gm.ascent + gm.descent);
 						gm = font.glyphBBox(gl.glyph, chsd / 10.0);
 						realAsce = qMax(realAsce, gm.ascent + gm.descent);
-						wide += gm.width;
+						wide += gm.width; // a.l.e: this sets the width of the indent
 					}
 					wide = (wide* scaleH) + (1 - scaleH);
 					realAsce = realAsce  * scaleV + offset;
diff --git a/scribus/scribus.cpp b/scribus/scribus.cpp
index 4330c79081..4eb0b3aab3 100644
--- a/scribus/scribus.cpp
+++ b/scribus/scribus.cpp
@@ -61,6 +61,8 @@ for which a new license (GPL+exception) is in place.
 //#include <QQmlProperty>
 //>>
 #include <QRegExp>
+#include <memory>
+#include <QScopedPointer>
 #include <QScopedPointer>
 #include <QStyleFactory>
 #include <QTableWidget>
@@ -259,6 +261,7 @@ for which a new license (GPL+exception) is in place.
 #include "undostate.h"
 #include "units.h"
 #include "urllauncher.h"
+#include "usertaskstructs.h"
 #include "util.h"
 #include "util_file.h"
 #include "util_formats.h"
@@ -6432,8 +6435,7 @@ void ScribusMainWindow::duplicateItem()
 	if (UndoManager::undoEnabled())
 		trans = m_undoManager->beginTransaction(Um::Selection, Um::IPolygon, Um::Duplicate, "", Um::IMultipleDuplicate);
 
-	ItemMultipleDuplicateData mdData;
-	memset(&mdData, 0, sizeof(mdData));
+	ItemMultipleDuplicateData mdData{};
 	mdData.type = 0;
 	mdData.copyCount = 1;
 	mdData.copyShiftOrGap = 0;
@@ -6469,7 +6471,7 @@ void ScribusMainWindow::duplicateItemMulti()
 	if (doc->appMode == modeEditClip)
 		view->requestMode(submodeEndNodeEdit);
 	internalCopy = true;
-	QScopedPointer<MultipleDuplicate> dia(new MultipleDuplicate(doc->unitIndex(), this));
+	QScopedPointer<MultipleDuplicate> dia(new MultipleDuplicate(this, doc));
 	if (dia->exec())
 	{
 		ItemMultipleDuplicateData mdData;
diff --git a/scribus/scribusdoc.cpp b/scribus/scribusdoc.cpp
index e2e5da6fca..1e37b004cc 100644
--- a/scribus/scribusdoc.cpp
+++ b/scribus/scribusdoc.cpp
@@ -38,6 +38,7 @@ for which a new license (GPL+exception) is in place.
 #include <QProgressBar>
 #include <QtAlgorithms>
 #include <QTime>
+#include <QStringList>
 //#include <qtconcurrentmap.h>
 
 #include "actionmanager.h"
@@ -13500,10 +13501,14 @@ QRectF ScribusDoc::ApplyGridF(const QRectF& in)
 	return nr;
 }
 
-void ScribusDoc::itemSelection_MultipleDuplicate(ItemMultipleDuplicateData& mdData)
+void ScribusDoc::itemSelection_MultipleDuplicate(const ItemMultipleDuplicateData& mdData)
 {
 	if ((mdData.type==0 && mdData.copyCount<1) || (mdData.type==1 && (mdData.gridRows==1 && mdData.gridCols==1)))
 		return;
+	if ((mdData.type == 2) && (mdData.pageSelection != 4) && (currentPageNumber() == Pages->count() - 1))
+		// cannot copy on following pages, while being on the last page
+		return;
+
 	QString tooltip;
 	UndoTransaction activeTransaction;
 
@@ -13631,6 +13636,10 @@ void ScribusDoc::itemSelection_MultipleDuplicate(ItemMultipleDuplicateData& mdDa
 		QString vString = QString::number(mdData.gridGapV, 'f', unitPrecision) + " " + unitSuffix;
 		tooltip = tr("Number of copies: %1\nHorizontal gap: %2\nVertical gap: %3").arg(copyCount-1).arg(hString).arg(vString).arg(unitSuffix);
 	}
+	else if (mdData.type==2)
+	{
+		multipleDuplicateByPage(mdData, selection, tooltip);
+	}
 	if (activeTransaction)
 	{
 		activeTransaction.commit("", nullptr, "", tooltip, nullptr);
@@ -13643,6 +13652,76 @@ void ScribusDoc::itemSelection_MultipleDuplicate(ItemMultipleDuplicateData& mdDa
 	changed();
 }
 
+void ScribusDoc::multipleDuplicateByPage(const ItemMultipleDuplicateData& dialogData, Selection& selection, QString& tooltip)
+{
+	std::vector<int> pages;
+	QString pageRange;
+	if (dialogData.pageSelection == 1) {
+		pageRange = QString("%1-%2").arg(currentPageNumber() + 2).arg(Pages->count());
+	} else if ((dialogData.pageSelection == 2) || dialogData.pageSelection == 3) {
+		int start = currentPageNumber() + 2;
+		// round to the next odd / even number
+		if (dialogData.pageSelection == 2) {
+			start += start % 2;
+		} else {
+			start += 1 - (start % 2);
+		}
+		QStringList pageList{};
+		for (int i = start; i <= Pages->count(); i += 2) {
+			pageList << QString::number(i);
+		}
+		pageRange = pageList.join(',');
+	} else if (dialogData.pageSelection == 4) {
+		pageRange = dialogData.pageRange;
+	}
+	parsePagesString(pageRange, &pages, Pages->count());
+
+	PageItem* lastInChain = nullptr;
+	if (dialogData.pageLinkText) {
+		for (auto item: selection.items()) {
+			// get the first text frame in the selection, the first frame in the chain
+			if (item->itemType() == PageItem::TextFrame) {
+				if (item->isInChain() && item->isAllNextInChainSamePage()) {
+					lastInChain = item->lastInChain();
+					assert( !lastInChain->nextInChain() );
+				} else {
+					lastInChain = item;
+				}
+				break;
+			}
+		}
+	}
+
+	ScPage* oldCurrentPage = currentPage();
+	ScriXmlDoc xmlStream;
+	QString buffer = xmlStream.WriteElem(this, &selection);
+	for (const auto page: pages) {
+		ScPage* targetPage = Pages->at(page - 1);
+		setCurrentPage(targetPage);
+		auto countBeforeInsert = Items->count();
+		xmlStream.ReadElem(buffer, m_appPrefsData.fontPrefs.AvailFonts, this, currentPage()->xOffset(), currentPage()->yOffset(), false, true, m_appPrefsData.fontPrefs.GFontSub);
+		if (lastInChain) {
+			for (auto i = countBeforeInsert; i < Items->count(); ++i) {
+				auto item = Items->at(i);
+				if (item->itemType() == PageItem::TextFrame) {
+					if (item->isInChain()) {
+						lastInChain->link(item->firstInChain());
+						lastInChain = item->lastInChain();
+					} else {
+						lastInChain->link(item);
+						lastInChain = item;
+					}
+					break;
+				}
+			}
+		}
+	}
+
+	setCurrentPage(oldCurrentPage);
+
+	tooltip = tr("Copied %1 items on %2 pages").arg(selection.count()).arg(pages.size());
+}
+
 
 void ScribusDoc::itemSelection_ApplyImageEffects(ScImageEffectList& newEffectList, Selection* customSelection)
 {
diff --git a/scribus/scribusdoc.h b/scribus/scribusdoc.h
index 8ddb018bea..0bd0c1ff44 100644
--- a/scribus/scribusdoc.h
+++ b/scribus/scribusdoc.h
@@ -1512,7 +1512,7 @@ public slots:
 	void itemSelection_DistributeTop();
 	void itemSelection_SwapLeft();
 	void itemSelection_SwapRight();
-	void itemSelection_MultipleDuplicate(ItemMultipleDuplicateData&);
+	void itemSelection_MultipleDuplicate(const ItemMultipleDuplicateData&);
 	void itemSelection_UniteItems(Selection* customSelection = nullptr);
 	void itemSelection_SplitItems(Selection* customSelection = nullptr);
 	/**
@@ -1693,6 +1693,8 @@ public slots:
 	//flags used for indicating needs of updates
 	bool m_flag_notesChanged;
 
+	void multipleDuplicateByPage(const ItemMultipleDuplicateData& mdData, Selection& selection, QString& tooltip);
+
 public:
 	const QList<Mark*> marksList() { return m_docMarksList; }
 	const QList<TextNote*> notesList() { return m_docNotesList; }
diff --git a/scribus/ui/multipleduplicate.cpp b/scribus/ui/multipleduplicate.cpp
index eb5a65aec7..1834486d6e 100644
--- a/scribus/ui/multipleduplicate.cpp
+++ b/scribus/ui/multipleduplicate.cpp
@@ -13,21 +13,24 @@ for which a new license (GPL+exception) is in place.
 #include <QSpinBox>
 #include <QTabWidget>
 
+#include "iconmanager.h"
+#include "scribusdoc.h"
 #include "scrspinbox.h"
 #include "units.h"
+#include "ui/createrange.h"
 #include "usertaskstructs.h"
 
-MultipleDuplicate::MultipleDuplicate( int unitIndex, QWidget* parent) : QDialog(parent), m_unitIndex(unitIndex)
+MultipleDuplicate::MultipleDuplicate(QWidget* parent, ScribusDoc *doc) : QDialog(parent), m_Doc(doc)
 {
 	setupUi(this);
 
-	m_unitRatio = unitGetRatioFromIndex(m_unitIndex);
+	m_unitRatio = unitGetRatioFromIndex(m_Doc->unitIndex());
 
 	//set up mspinboxes
-	horizShiftSpinBox->setNewUnit(unitIndex);
-	vertShiftSpinBox->setNewUnit(unitIndex);
-	horizRCGapSpinBox->setNewUnit(unitIndex);
-	vertRCGapSpinBox->setNewUnit(unitIndex);
+	horizShiftSpinBox->setNewUnit(m_Doc->unitIndex());
+	vertShiftSpinBox->setNewUnit(m_Doc->unitIndex());
+	horizRCGapSpinBox->setNewUnit(m_Doc->unitIndex());
+	vertRCGapSpinBox->setNewUnit(m_Doc->unitIndex());
 	horizShiftSpinBox->setMinimum(-1000);
 	vertShiftSpinBox->setMinimum(-1000);
 	horizRCGapSpinBox->setMinimum(-1000);
@@ -43,9 +46,16 @@ MultipleDuplicate::MultipleDuplicate( int unitIndex, QWidget* parent) : QDialog(
 	
 	createGapRadioButton->setChecked(true);
 	setCopiesGap();
+
+	toolButtonPageRange->setIcon(IconManager::instance()->loadIcon("ellipsis.png"));
+	radioButtonPageAll->setChecked(true);
+
 	// signals and slots connections
 	connect(createGapRadioButton, SIGNAL(clicked()), this, SLOT(setCopiesGap()));
 	connect(shiftCreatedItemsRadioButton, SIGNAL(clicked()), this, SLOT(setCopiesShift()));
+
+	connect(lineEditPageRange, &QLineEdit::textChanged, this, &MultipleDuplicate::selectRangeOfPages);
+	connect(toolButtonPageRange, &QToolButton::clicked, this, &MultipleDuplicate::createPageNumberRange);
 }
 
 MultipleDuplicate::~MultipleDuplicate()
@@ -76,4 +86,36 @@ void MultipleDuplicate::getMultiplyData(ItemMultipleDuplicateData& mdData)
 	mdData.gridCols = gridColsSpinBox->value();
 	mdData.gridGapH = horizRCGapSpinBox->value();
 	mdData.gridGapV = vertRCGapSpinBox->value();
+	if (radioButtonPageAll->isChecked()) {
+		mdData.pageSelection = 1;
+	} else if (radioButtonPageEven->isChecked()) {
+		mdData.pageSelection = 2;
+	} else if (radioButtonPageOdd->isChecked()) {
+		mdData.pageSelection = 3;
+	} else if (radioButtonPageRange->isChecked()) {
+		mdData.pageSelection = 4;
+	}
+	mdData.pageRange = lineEditPageRange->text();
+	mdData.pageLinkText = checkBoxPageLinkText->isChecked();
+}
+
+void MultipleDuplicate::selectRangeOfPages()
+{
+	radioButtonPageRange->setChecked(true);
+}
+
+void MultipleDuplicate::createPageNumberRange()
+{
+	if (m_Doc!=0)
+	{
+		CreateRange cr(lineEditPageRange->text(), m_Doc->Pages->count(), this);
+		if (cr.exec())
+		{
+			CreateRangeData crData;
+			cr.getCreateRangeData(crData);
+			lineEditPageRange->setText(crData.pageRange);
+			return;
+		}
+	}
+	lineEditPageRange->setText(QString::null);
 }
diff --git a/scribus/ui/multipleduplicate.h b/scribus/ui/multipleduplicate.h
index c8abb74b4e..36c1bd1580 100644
--- a/scribus/ui/multipleduplicate.h
+++ b/scribus/ui/multipleduplicate.h
@@ -9,20 +9,25 @@ for which a new license (GPL+exception) is in place.
 
 #include "ui_multipleduplicate.h"
 struct ItemMultipleDuplicateData;
+class ScribusDoc;
 
 class MultipleDuplicate : public QDialog, Ui::MultipleDuplicate
 {
 	Q_OBJECT
 	public:
-		MultipleDuplicate(int unitIndex, QWidget* parent);
+        // InsertAFrame(QWidget* parent, ScribusDoc *doc);
+		MultipleDuplicate(QWidget* parent, ScribusDoc *doc);
 		~MultipleDuplicate();
 		void getMultiplyData(ItemMultipleDuplicateData&);
 	protected:
-		int m_unitIndex;
+		// int m_unitIndex;
+		ScribusDoc *m_Doc;
 		double m_unitRatio;
 	protected slots:
 		void setCopiesShift();
 		void setCopiesGap();
+        void selectRangeOfPages();
+        void createPageNumberRange();
 };
 
 #endif
diff --git a/scribus/ui/multipleduplicate.ui b/scribus/ui/multipleduplicate.ui
index cb3524b2c3..dc12a729dd 100644
--- a/scribus/ui/multipleduplicate.ui
+++ b/scribus/ui/multipleduplicate.ui
@@ -350,6 +350,95 @@
        </item>
       </layout>
      </widget>
+     <widget class="QWidget" name="tab3">
+      <attribute name="title">
+       <string>By &amp;Page</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_8">
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_7">
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageAll">
+           <property name="text">
+            <string>&amp;All Following Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageEven">
+           <property name="text">
+            <string>All Follwing &amp;Even Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageOdd">
+           <property name="text">
+            <string>All Following &amp;Odd Pages</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <widget class="QRadioButton" name="radioButtonPageRange">
+           <property name="text">
+            <string>Ran&amp;ge of Pages:</string>
+           </property>
+           <attribute name="buttonGroup">
+            <string notr="true">buttonGroup</string>
+           </attribute>
+          </widget>
+         </item>
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout">
+           <item>
+            <widget class="QLineEdit" name="lineEditPageRange"/>
+           </item>
+           <item>
+            <widget class="QToolButton" name="toolButtonPageRange">
+             <property name="text">
+              <string>...</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_9">
+         <item>
+          <widget class="QCheckBox" name="checkBoxPageLinkText">
+           <property name="text">
+            <string>&amp;Link Text Frames</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_5">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
     </widget>
    </item>
    <item>
@@ -468,4 +557,7 @@
    </hints>
   </connection>
  </connections>
+ <buttongroups>
+  <buttongroup name="buttonGroup"/>
+ </buttongroups>
 </ui>
diff --git a/scribus/usertaskstructs.h b/scribus/usertaskstructs.h
index bffd02d241..1d5a0cd0f9 100644
--- a/scribus/usertaskstructs.h
+++ b/scribus/usertaskstructs.h
@@ -34,16 +34,19 @@ struct InsertAFrameData
 
 struct ItemMultipleDuplicateData
 {
-	int type;
-	int copyCount;
-	int copyShiftOrGap;
-	double copyShiftGapH;
-	double copyShiftGapV;
-	double copyRotation;
-	int gridRows;
-	int gridCols;
-	double gridGapH;
-	double gridGapV;
+	int type = 0;
+	int copyCount = 0;
+	int copyShiftOrGap = 0;
+	double copyShiftGapH = 0.0;
+	double copyShiftGapV = 0.0;
+	double copyRotation = 0.0;
+	int gridRows = 0;
+	int gridCols = 0;
+	double gridGapH = 0.0;
+	double gridGapV = 0.0;
+	int pageSelection = 0; // 1 = All, 2 = Even, 3 = odd, 4 = Range
+	QString pageRange;
+	bool pageLinkText = false;
 };
 
 struct CreateRangeData
