From 1117015d0796db213b880157a4fe0b758967ce58 Mon Sep 17 00:00:00 2001
From: Fahad <fahad.alsaidi@gmail.com>
Date: Tue, 15 May 2018 10:34:36 +0400
Subject: add basic font fallback support

This is a basic font fallback support feature. Right now, it takes one
font but in future it may takes more than one font. Fallback font is not
saved in scribus format, thus once you set fallback font, missing
faces's font style will change.

diff --git a/scribus/text/itextsource.h b/scribus/text/itextsource.h
index 41c88d4..f7cdd0e 100644
--- a/scribus/text/itextsource.h
+++ b/scribus/text/itextsource.h
@@ -51,6 +51,7 @@ public:
 	
 	virtual const ITextSource* parent() const { return nullptr; }
 	virtual int parentPos() const { return 0; }
+    virtual void setMissingFaces(QList<GlyphCluster> &missingFacesList) = 0;
 	
 	const ITextSource* original() const
     {
diff --git a/scribus/text/storytext.cpp b/scribus/text/storytext.cpp
index 8276c93..481ddce 100644
--- a/scribus/text/storytext.cpp
+++ b/scribus/text/storytext.cpp
@@ -2002,6 +2002,35 @@ void StoryText::invalidate(int firstItem, int endItem)
 		emit changed(firstItem, endItem);
 }
 
+void StoryText::setMissingFaces(QList<GlyphCluster> &missingFacesList)
+{
+    m_missingFacesList = missingFacesList;
+}
+
+QList<GlyphCluster> StoryText::missingFaces() const
+{
+    return m_missingFacesList;
+}
+
+void StoryText::setFallBackFont(const QString font)
+{
+    m_fallBackFont = font;
+}
+
+QString StoryText::fallBackFont() const
+{
+    return m_fallBackFont;
+}
+
+void StoryText::setFallBackFontSize(const double size)
+{
+    m_fallBackFontSize = size;
+}
+
+double StoryText::fallBackFontSize() const
+{
+    return m_fallBackFontSize;
+}
 
 // physical view
 
diff --git a/scribus/text/storytext.h b/scribus/text/storytext.h
index 0693f51..d85aa70 100644
--- a/scribus/text/storytext.h
+++ b/scribus/text/storytext.h
@@ -36,6 +36,7 @@ pageitem.cpp  -  description
 #include "marks.h"
 #include "text/frect.h"
 #include "text/specialchars.h"
+#include "text/glyphcluster.h"
 #include "sctextstruct.h"
 #include "style.h"
 #include "styles/charstyle.h"
@@ -268,6 +269,16 @@ class SCRIBUS_API StoryText : public QObject, public SaxIO, public ITextSource
  	/// call this if the shape of the paragraph changes (redos layout)
  	void invalidateLayout();
 
+    // return all missing faces ( glyhp.id == 0)
+    QList<GlyphCluster> missingFaces() const;
+    void setMissingFaces(QList<GlyphCluster> &missingFacesList);
+    // return the fallback font for this story text
+    QString fallBackFont() const;
+    void setFallBackFont(const QString font);
+    // return the size of the size of the fallback font
+    void setFallBackFontSize(const double size);
+    double fallBackFontSize() const;
+
 public slots:
 	/// call this if some logical style changes (redos shaping and layout)
  	void invalidateAll();
@@ -309,6 +320,9 @@ private:
 // 	uint layouterVersion;
  	/// is true after layout() has been exercised
 // 	bool layouterValid;
+    QList<GlyphCluster> m_missingFacesList;
+    QString m_fallBackFont;
+    double m_fallBackFontSize = 0;
  };
 
 
diff --git a/scribus/text/textshaper.cpp b/scribus/text/textshaper.cpp
index 53983a5..4be6b46 100644
--- a/scribus/text/textshaper.cpp
+++ b/scribus/text/textshaper.cpp
@@ -472,6 +472,10 @@ ShapedText TextShaper::shape(int fromPos, int toPos)
 			{
 				GlyphLayout gl;
 				gl.glyph = glyphs[i].codepoint;
+
+                if (gl.glyph == 0)
+                    m_missingFacesList.append(run);
+
 				if (gl.glyph == 0 ||
 				    (ch == SpecialChars::LINEBREAK || ch == SpecialChars::PARSEP ||
 				     ch == SpecialChars::FRAMEBREAK || ch == SpecialChars::COLBREAK))
@@ -640,6 +644,7 @@ ShapedText TextShaper::shape(int fromPos, int toPos)
 
 	m_textMap.clear();
 	m_text = "";
+    m_story.setMissingFaces(m_missingFacesList);
 	result.needsContext(m_contextNeeded);
 	return result;
 }
diff --git a/scribus/text/textshaper.h b/scribus/text/textshaper.h
index fa97dff..65289fe 100644
--- a/scribus/text/textshaper.h
+++ b/scribus/text/textshaper.h
@@ -68,6 +68,7 @@ private:
 	bool m_singlePar;
 	QString m_text;
 	QMap<int, int> m_textMap;
+    QList<GlyphCluster> m_missingFacesList;
 };
 
 #endif // TEXTSHAPER_H
diff --git a/scribus/ui/propertywidget_advanced.cpp b/scribus/ui/propertywidget_advanced.cpp
index 0bb755d..17239eb 100644
--- a/scribus/ui/propertywidget_advanced.cpp
+++ b/scribus/ui/propertywidget_advanced.cpp
@@ -10,10 +10,12 @@ for which a new license (GPL+exception) is in place.
 #include "appmodes.h"
 #include "iconmanager.h"
 #include "pageitem_table.h"
+#include "prefsmanager.h"
 #include "scribus.h"
 #include "scribusdoc.h"
 #include "selection.h"
 #include "units.h"
+#include "util.h"
 
 PropertyWidget_Advanced::PropertyWidget_Advanced(QWidget* parent) : QFrame(parent)
 {
@@ -45,6 +47,11 @@ PropertyWidget_Advanced::PropertyWidget_Advanced(QWidget* parent) : QFrame(paren
 	minGlyphExtensionLabel->setBuddy(minGlyphExtSpinBox);
 	maxGlyphExtensionLabel->setBuddy(maxGlyphExtSpinBox);
 
+    fallBackFont->setMaximumSize(190, 30);
+    PrefsManager* prefsManager = PrefsManager::instance();
+    fontFallBackSize->setSuffix( unitGetSuffixFromIndex(SC_POINTS) );
+    fontFallBackSize->setValue(prefsManager->appPrefs.itemToolPrefs.textSize / 10.0);
+
 	languageChange();
 }
 
@@ -128,6 +135,8 @@ void PropertyWidget_Advanced::connectSignals()
 	connect(normWordTrackingSpinBox, SIGNAL(valueChanged(double)), this, SLOT(handleNormWordTracking()) );
 	connect(minGlyphExtSpinBox     , SIGNAL(valueChanged(double)), this, SLOT(handleMinGlyphExtension()) );
 	connect(maxGlyphExtSpinBox     , SIGNAL(valueChanged(double)), this, SLOT(handleMaxGlyphExtension()) );
+    connect(fallBackFont			, SIGNAL(activated(const QString &)), this, SLOT(handleFontFallBack(const QString &)));
+    connect(fontFallBackSize, SIGNAL(valueChanged(double)), this, SLOT(handleFontFallBackSize(double)));
 }
 
 void PropertyWidget_Advanced::disconnectSignals()
@@ -140,6 +149,8 @@ void PropertyWidget_Advanced::disconnectSignals()
 	disconnect(normWordTrackingSpinBox, SIGNAL(valueChanged(double)), this, SLOT(handleNormWordTracking()) );
 	disconnect(minGlyphExtSpinBox     , SIGNAL(valueChanged(double)), this, SLOT(handleMinGlyphExtension()) );
 	disconnect(maxGlyphExtSpinBox     , SIGNAL(valueChanged(double)), this, SLOT(handleMaxGlyphExtension()) );
+    disconnect(fallBackFont			, SIGNAL(activated(const QString &)), this, SLOT(handleFontFallBack(const QString &)) );
+    disconnect(fontFallBackSize		, SIGNAL(valueChanged(double)), this, SLOT(handleFontFallBackSize(double)));
 }
 
 void PropertyWidget_Advanced::configureWidgets(void)
@@ -197,6 +208,26 @@ void PropertyWidget_Advanced::showTracking(double e)
 	tracking->showValue(e / 10.0);
 }
 
+void PropertyWidget_Advanced::showFontFallBack(const QString &font)
+{
+    if (!m_ScMW || !m_item || m_ScMW->scriptIsRunning())
+        return;
+    if (m_item->itemText.fallBackFont().isEmpty())
+        setCurrentComboItem(fallBackFont, font);
+    else
+        setCurrentComboItem(fallBackFont, m_item->itemText.fallBackFont());
+}
+
+void PropertyWidget_Advanced::showFontFallBackSize(double s)
+{
+    if (!m_ScMW || !m_item || m_ScMW->scriptIsRunning())
+        return;
+    if (m_item->itemText.fallBackFontSize() != 0)
+        fontFallBackSize->showValue(m_item->itemText.fallBackFontSize());
+    else
+        fontFallBackSize->showValue(s / 10.0);
+}
+
 void PropertyWidget_Advanced::handleBaselineOffset()
 {
 	if (!m_doc || !m_item || !m_ScMW || m_ScMW->scriptIsRunning())
@@ -325,6 +356,50 @@ void PropertyWidget_Advanced::handleTracking()
 	}
 }
 
+void PropertyWidget_Advanced::handleFontFallBack(const QString &font)
+{
+    if (!m_doc || !m_item || !m_ScMW || m_ScMW->scriptIsRunning())
+        return;
+    m_item->itemText.setFallBackFont(font);
+    if (m_missingfaceslist.isEmpty())
+        m_missingfaceslist = m_item->itemText.missingFaces();
+
+    if (!m_missingfaceslist.isEmpty())
+    {
+        CharStyle charStyle;
+        charStyle.setFont((*m_doc->AllFonts)[font]);
+
+        foreach (const GlyphCluster& textRun, m_missingfaceslist) {
+            m_item->itemText.applyCharStyle(textRun.firstChar(), textRun.lastChar() - textRun.firstChar() + 1, charStyle);
+        }
+
+        m_item->itemText.invalidateAll();
+        m_doc->changed();
+        m_doc->regionsChanged()->update(QRectF());
+    }
+
+}
+
+void PropertyWidget_Advanced::handleFontFallBackSize(double s)
+{
+    if (!m_doc || !m_item || !m_ScMW || m_ScMW->scriptIsRunning())
+        return;
+    m_item->itemText.setFallBackFontSize(s);
+    if (!m_missingfaceslist.isEmpty())
+    {
+        CharStyle charStyle;
+        charStyle.setFontSize(qRound(fontFallBackSize->value() * 10.0));
+
+        foreach (const GlyphCluster& textRun, m_missingfaceslist) {
+            m_item->itemText.applyCharStyle(textRun.firstChar(), textRun.lastChar() - textRun.firstChar() + 1, charStyle);
+        }
+
+        m_item->itemText.invalidateAll();
+        m_doc->changed();
+        m_doc->regionsChanged()->update(QRectF());
+    }
+}
+
 void PropertyWidget_Advanced::updateCharStyle(const CharStyle& charStyle)
 {
 	if (!m_ScMW || m_ScMW->scriptIsRunning())
@@ -334,6 +409,8 @@ void PropertyWidget_Advanced::updateCharStyle(const CharStyle& charStyle)
 	showTextScaleV(charStyle.scaleV());
 	showTracking(charStyle.tracking());
 	showBaseLineOffset(charStyle.baselineOffset());
+    showFontFallBack(charStyle.font().family() + " " + charStyle.font().style());
+    showFontFallBackSize(charStyle.fontSize());
 
 	normWordTrackingSpinBox->showValue(charStyle.wordTracking() * 100.0);
 }
diff --git a/scribus/ui/propertywidget_advanced.h b/scribus/ui/propertywidget_advanced.h
index 2ab15fa..6bf468a 100644
--- a/scribus/ui/propertywidget_advanced.h
+++ b/scribus/ui/propertywidget_advanced.h
@@ -8,8 +8,8 @@ for which a new license (GPL+exception) is in place.
 #define PROPERTYWIDGET_ADVANCED_H
 
 #include "ui_propertywidget_advancedbase.h"
-
 #include "propertywidgetbase.h"
+#include "text/glyphcluster.h"
 
 class CharStyle;
 class ParagraphStyle;
@@ -53,6 +53,8 @@ public slots:
 	void showTextScaleH(double e);
 	void showTextScaleV(double e);
 	void showTracking(double e);
+    void showFontFallBack(const QString& font);
+    void showFontFallBackSize(double s);
 
 	void updateCharStyle(const CharStyle& charStyle);
 	void updateStyle(const ParagraphStyle& newCurrent);
@@ -66,7 +68,10 @@ private slots:
 	void handleTextScaleH();
 	void handleTextScaleV();
 	void handleTracking();
-
+    void handleFontFallBack(const QString &font);
+   void handleFontFallBackSize(double s);
+private:
+   QList<GlyphCluster> m_missingfaceslist;
 };
 
 #endif
diff --git a/scribus/ui/propertywidget_advancedbase.ui b/scribus/ui/propertywidget_advancedbase.ui
index baafb21..cbf6109 100644
--- a/scribus/ui/propertywidget_advancedbase.ui
+++ b/scribus/ui/propertywidget_advancedbase.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>239</width>
-    <height>155</height>
+    <width>252</width>
+    <height>269</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -227,6 +227,27 @@
      </property>
     </widget>
    </item>
+   <item row="6" column="0" colspan="2">
+    <widget class="QLabel" name="fontFallBackLabel">
+     <property name="text">
+      <string>Font Fallback</string>
+     </property>
+    </widget>
+   </item>
+   <item row="7" column="0" colspan="3">
+    <widget class="FontCombo" name="fallBackFont" native="true">
+     <property name="toolTip">
+      <string>Set Fallback Font fot the Text Frame</string>
+     </property>
+    </widget>
+   </item>
+   <item row="8" column="0" colspan="3">
+    <widget class="ScrSpinBox" name="fontFallBackSize">
+     <property name="toolTip">
+      <string>Set Font Size for font Fallback</string>
+     </property>
+    </widget>
+   </item>
   </layout>
  </widget>
  <customwidgets>
@@ -235,6 +256,11 @@
    <extends>QDoubleSpinBox</extends>
    <header>ui/scrspinbox.h</header>
   </customwidget>
+  <customwidget>
+   <class>FontCombo</class>
+   <extends>QWidget</extends>
+   <header>ui/fontcombo.h</header>
+  </customwidget>
  </customwidgets>
  <resources/>
  <connections/>
