Bug 163215 - Kashida justification does not work with AAT fonts
Summary: Kashida justification does not work with AAT fonts
Status: RESOLVED FIXED
Alias: None
Product: LibreOffice
Classification: Unclassified
Component: graphics stack (show other bugs)
Version:
(earliest affected)
unspecified
Hardware: All All
: medium normal
Assignee: Jonathan Clark
URL:
Whiteboard: target:25.2.0
Keywords:
Depends on:
Blocks: Kashida-Justification, Tatweel
  Show dependency treegraph
 
Reported: 2024-09-30 11:37 UTC by ⁨خالد حسني⁩
Modified: 2024-10-18 19:49 UTC (History)
3 users (show)

See Also:
Crash report or crash signature:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description ⁨خالد حسني⁩ 2024-09-30 11:37:33 UTC
Currently we verify Kashida insertion positions with HarfBuzz to drop positions where it is not safe to insert kashida without re-shaping (as we insert Kashida after shaping). This works fine for OpenType fonts, but HarfBuzz does not (and can not) do any such validation for AAT fonts, so all Kashida insertion positions are considered invalid for such fonts and we get no Kashida insertion at all.

AAT Arabic fonts are rather common on macOS (all Arabic fonts shipped by Apple seem to be AAT fonts). A suggested fix is to not do any Kashida position validation for AAT fonts, so basically revert to the old behavior. Almost all Arabic AAT fonts are rather simple ones that do not do any complex substitutions, so the validation is superfluous and no validation is better than no Kashida justification.

For testing, if access to macOS system fonts is not possible, http://wiki.irmug.com/index.php/X_Series_2 fonts can be used as they are hyprid OpenType/AAT fonts (and HarfBuzz will use the AAT layout table so OpenType layout tables are effectively ignored).
Comment 1 Eyal Rozenberg 2024-09-30 11:51:32 UTC
> Almost all Arabic AAT fonts are rather simple

Is it possible to distinguish rather-simple from not-simple fonts?
Comment 2 ⁨خالد حسني⁩ 2024-09-30 12:29:34 UTC
(In reply to Eyal Rozenberg from comment #1)
> > Almost all Arabic AAT fonts are rather simple
> 
> Is it possible to distinguish rather-simple from not-simple fonts?

Not to my knowledge. Unlike OpenType where the shaping is split between the font and the shaping engine, AAT font do everything themselves (using a state machine) and the shaping engine executes it without much knowledge about the writing system, so simple and complex fonts all have a state machine that does something.
Comment 3 Buovjaga 2024-09-30 14:50:19 UTC
-> NEW
Comment 4 Julien Nabet 2024-09-30 19:45:54 UTC
Khaled: I thought about a first part to create OutputDevice::HasAatFeatures that would be then called in editeng/source/editeng/impedit3.cxx

Here's the patch:
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index 3f534c50b29a..4a76bdaa18bc 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -1159,6 +1159,8 @@ public:
     sal_Int32                   HasGlyphs( const vcl::Font& rFont, std::u16string_view rStr,
                                            sal_Int32 nIndex = 0, sal_Int32 nLen = -1 ) const;
 
+    bool                        HasAatFeatures() const;
+
     tools::Long                        GetMinKashida() const;
 
     // i60594
diff --git a/vcl/inc/font/FeatureCollector.hxx b/vcl/inc/font/FeatureCollector.hxx
index 1b71d021599d..d4c6fe4160cd 100644
--- a/vcl/inc/font/FeatureCollector.hxx
+++ b/vcl/inc/font/FeatureCollector.hxx
@@ -12,6 +12,7 @@
 
 #include <vcl/font/Feature.hxx>
 #include <hb.h>
+#include <hb-aat.h>
 #include <i18nlangtag/languagetag.hxx>
 
 #include <font/PhysicalFontFace.hxx>
@@ -42,6 +43,7 @@ private:
 
 public:
     bool collect();
+    bool hasAatFeatures() { return hb_aat_layout_has_substitution(m_pHbFace); };
 };
 
 } // namespace vcl::font
diff --git a/vcl/source/outdev/font.cxx b/vcl/source/outdev/font.cxx
index a2f32327f72c..75ad8b24f6cf 100644
--- a/vcl/source/outdev/font.cxx
+++ b/vcl/source/outdev/font.cxx
@@ -171,6 +171,22 @@ bool OutputDevice::GetFontFeatures(std::vector<vcl::font::Feature>& rFontFeature
     return true;
 }
 
+bool OutputDevice::HasAatFeatures() const
+{
+    if (!ImplNewFont())
+        return false;
+
+    LogicalFontInstance* pFontInstance = mpFontInstance.get();
+    if (!pFontInstance)
+        return false;
+
+    const LanguageTag& rOfficeLanguage = Application::GetSettings().GetUILanguageTag();
+
+    std::vector<vcl::font::Feature> vFontFeatures;
+    vcl::font::FeatureCollector aFeatureCollector(pFontInstance->GetFontFace(), vFontFeatures, rOfficeLanguage);
+    return aFeatureCollector.hasAatFeatures();
+}
+
 FontMetric OutputDevice::GetFontMetric() const
 {
     FontMetric aMetric;

Does it seem reasonable or is there a simpler way?

(of course, I may clean the duplicated code in OutputDevice::HasAatFeatures with a lcl function but it's just for the idea).
Comment 5 ⁨خالد حسني⁩ 2024-09-30 21:14:10 UTC
I’m not very up to date with the Kashida code, but I was thinking of something simpler, like in https://git.libreoffice.org/core/+/refs/heads/master/vcl/source/gdi/CommonSalLayout.cxx#687 always set GlyphItemFlags::IS_SAFE_TO_INSERT_KASHIDA flag if the font has AAT substitutions, so that down stream code does not require any changes. Since Jonathan Clark is actively working on this code, he is more suited to give an opinion here.
Comment 6 Jonathan Clark 2024-09-30 22:03:57 UTC
(In reply to ⁨خالد حسني⁩ from comment #5)
> I’m not very up to date with the Kashida code, but I was thinking of
> something simpler, like in
> https://git.libreoffice.org/core/+/refs/heads/master/vcl/source/gdi/
> CommonSalLayout.cxx#687 always set GlyphItemFlags::IS_SAFE_TO_INSERT_KASHIDA
> flag if the font has AAT substitutions, so that down stream code does not
> require any changes. Since Jonathan Clark is actively working on this code,
> he is more suited to give an opinion here.

In my opinion:

- Flag all glyphs as safe to insert kashida, as suggested here.

- Update OutputDevice::GetWordKashidaPositions (vcl/source/outdev/font.cxx:1218) to leave the position vector empty for AAT fonts.

- Update the fallback rule 8 (i18nutil/source/utility/kashida.cxx:297) to require a non-empty pValidPositions vector. We can't rely on VCL to filter out invalid positions for AAT fonts, so this fallback is not a safe default anymore.

It shouldn't be necessary to change any code in sw or editeng.
Comment 7 Commit Notification 2024-10-18 19:49:34 UTC
Jonathan Clark committed a patch related to this issue.
It has been pushed to "master":

https://git.libreoffice.org/core/commit/224fae69b224d28a1664c48117e77265ed67a136

tdf#163215: Enable kashida justification for AAT fonts

It will be available in 25.2.0.

The patch should be included in the daily builds available at
https://dev-builds.libreoffice.org/daily/ in the next 24-48 hours. More
information about daily builds can be found at:
https://wiki.documentfoundation.org/Testing_Daily_Builds

Affected users are encouraged to test the fix and report feedback.