Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bidi + Font Shaping Support #198

Merged
merged 340 commits into from
Nov 10, 2021
Merged

Bidi + Font Shaping Support #198

merged 340 commits into from
Nov 10, 2021

Conversation

JimBobSquarePants
Copy link
Member

@JimBobSquarePants JimBobSquarePants commented Aug 26, 2021

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

Implementation of bidi layout support + glyph shaping. Includes a complete rewrite of the layout engine.

Information and reference code adapted from
https://docs.microsoft.com/en-us/typography/opentype/spec/
https://github.com/harfbuzz/harfbuzz
https://github.com/foliojs/fontkit
https://github.com/LayoutFarm/Typography

Fixes #150, Fixes #66

Also provides the basic for supporting #8, and #9

Implemented features.

  • Mixed Bidi layout support LTR, RTL, LTR + RTL
  • LayoutMode.Horizontal
  • LayoutMode.Vertical
  • TextAlign
  • CodePoint Script class detection
  • Glyph Shaping
    • Default Shaper
    • Arabic Shaper
    • Indic Shaper (future)
    • Hangul Shaper (future)
    • Hebrew Shaper (future)
    • Khmer Shaper (future)
    • Thai Shaper (future)
    • Myanmar Shaper (future)
    • Universal Shaper (future)
    • Gsub Table
      • Gsub Format 1
      • Gsub Format 2
      • Gsub Format 3
      • Gsub Format 4
      • Gsub Format 5
      • Gsub Format 6
      • Gsub Format 7
      • Gsub Format 8
    • GPos Table
      • GPos Format 1
      • GPos Format 2
      • GPos Format 3
      • GPos Format 4
      • GPos Format 5
      • GPos Format 6
      • GPos Format 7
      • GPos Format 8
      • GPos Format 9

Example Output

Chinese Horizontal

horizontal-ltr

Chinese Vertical (columns are read right to left; the text direction is top to bottom)

vertical-rtl

Combined emoji (4 scalar values and 7 chars)

👩🏽‍🚒a

Arabic with complex ligatures

بِسْمِ ٱللَّهِ ٱلرَّحْمَٟنِ ٱلرَّحِيمِ

Mixed English and Arabic.

English اَلْعَرَبِيَّةُ English

Text alignment

😀A😀-left-bottom
😀A😀-left-center
😀A😀-left-top
😀A😀-center-bottom
😀A😀-center-center
😀A😀-center-top
😀A😀-right-bottom
😀A😀-right-center
😀A😀-right-top

Word wrapping (normal)

THISIS~1

Word wrapping (break all)

THISIS~1

Word wrapping (keep all)

THISIS~1

@codecov
Copy link

codecov bot commented Aug 27, 2021

Codecov Report

Merging #198 (3069f57) into master (68a3ee3) will increase coverage by 0.05%.
The diff coverage is 85.88%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #198      +/-   ##
==========================================
+ Coverage   87.29%   87.34%   +0.05%     
==========================================
  Files          99      177      +78     
  Lines        4651     7897    +3246     
  Branches      764     1240     +476     
==========================================
+ Hits         4060     6898    +2838     
- Misses        473      722     +249     
- Partials      118      277     +159     
Flag Coverage Δ
unittests 87.34% <85.88%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/SixLabors.Fonts/SystemFontCollection.cs 95.83% <ø> (ø)
.../Tables/AdvancedTypographic/AttachmentListTable.cs 0.00% <0.00%> (ø)
...AdvancedTypographic/GPos/NotImplementedSubTable.cs 0.00% <0.00%> (ø)
...AdvancedTypographic/GSub/NotImplementedSubTable.cs 0.00% <0.00%> (ø)
...bors.Fonts/Tables/General/CMap/Format14SubTable.cs 0.00% <0.00%> (ø)
...abors.Fonts/Tables/General/Colr/BaseGlyphRecord.cs 100.00% <ø> (ø)
...abors.Fonts/Tables/General/Kern/KerningSubTable.cs 52.94% <ø> (ø)
src/SixLabors.Fonts/Unicode/BidiClass.cs 62.50% <ø> (+0.96%) ⬆️
...c/SixLabors.Fonts/Unicode/BidiDictionary{T1,T2}.cs 75.00% <ø> (ø)
src/SixLabors.Fonts/Unicode/MappedArraySlice{T}.cs 100.00% <ø> (ø)
... and 122 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 68a3ee3...3069f57. Read the comment docs.

@JimBobSquarePants JimBobSquarePants mentioned this pull request Aug 29, 2021
4 tasks
@JimBobSquarePants
Copy link
Member Author

Hey @tocsoft !! I got combined emojis working

👩🏽‍🚒

@JimBobSquarePants
Copy link
Member Author

JimBobSquarePants commented Sep 8, 2021

There's gaps in some of the GSUB table support which I could do with help with completing. Lookups 5,6, & 8 which I'm sure someone with an understanding of C++ or Rust would be able to flesh out.

Complete implementations can be found here.
https://github.com/RazrFalcon/rustybuzz (Rust)
https://github.com/harfbuzz/harfbuzz (C++)

@JimBobSquarePants
Copy link
Member Author

@brianpopow @tocsoft

@mhmd-azeez has kindly volunteered to help keep us right with the Arabic shaping output so if you have any questions just ping him.

@brianpopow
Copy link
Contributor

Here are some first rendering results for text written from right to left:

farsi

@mhmd-azeez does this look correct to you?

@Gillibald
Copy link

Ah…. In the GsubTable we were already able to determine the Script for each individual codepoint. I hoped we’d be able to work like that rather than a single script per text block. How would we mix scripts?

Text needs to be itemized so each script run can be shaped separately. That also comes into play when you enable certain font features for a portion of the text.

@JimBobSquarePants JimBobSquarePants changed the title WIP : Bidi + Font Shaping Support Bidi + Font Shaping Support Oct 29, 2021
@JimBobSquarePants
Copy link
Member Author

@SixLabors/core I forgot to change the title but this is very much "ready to review"

@Gillibald
Copy link

Chinese Vertical (columns are read right to left; text direction is left to right) -> Chinese Vertical (columns are read right to left; text direction is top to bottom)

@JimBobSquarePants
Copy link
Member Author

Thanks @Gillibald just a bad copy/paste will fix. We’re definitely doing the right thing

@Gillibald
Copy link

You did a really great job here. So impressive. Hopefully, this will be adopted by many projects so this can improve even further. .Net is used everywhere so there should be first-class text processing without any extra native dependencies. Your work is the first starting point to make that possible.

Copy link
Member

@antonfirsov antonfirsov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bunch of random notes on the code without understanding what's happening.

Comment on lines 45 to 46
return;
// TODO: Test and fix tomorrow. Gsub Lookup 4.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore this file. It's become a bit of a dumping ground for experiments.

Shouldn't we redeclare samples/DrawWithImageSharp as tests/SandboxDrawWithImageSharp then?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I'll likely delete most of it during a cleanup though.

src/SixLabors.Fonts/RendererOptions.cs Outdated Show resolved Hide resolved
@@ -3,7 +3,6 @@

using System;
using System.Collections.Generic;
using System.Linq;
Copy link
Member

@antonfirsov antonfirsov Nov 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to the PR, but this type is confusing in context of our other libraries. Opened #212 and SixLabors/ImageSharp.Drawing#174

/// <param name="text">The text.</param>
/// <param name="options">The style.</param>
/// <returns>A collection of layout that describe all that's needed to measure or render a series of glyphs.</returns>
internal static TextLayout Default { get; set; } = new();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect this type to be inheritable and have other implementations than Default?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an odd one. TextMeasurer accepts the instance as a parameter and stores it as a private field. Not sure whether @tocsoft had plans to allow replacing parts.

src/SixLabors.Fonts/TextLayout.cs Show resolved Hide resolved
src/SixLabors.Fonts/Tables/General/CMapTable.cs Outdated Show resolved Hide resolved
return anchorFormat switch
{
1 => AnchorFormat1.Load(reader),
_ => throw new NotSupportedException($"anchorFormat {anchorFormat} not supported. Should be '1'.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need the abstraction and the type hierarchy here? Can't AnchorTable be a struct instead to make things faster?

/// The order of the records also corresponds to the writing direction — that is, the logical direction — of the text.
/// For text written left to right, the first component is on the left; for text written right to left, the first component is on the right.
/// </summary>
internal class LigatureAttachTable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see a bunch of simple ***Table, and similar classes:

  • Are not subclassed
  • Are small
  • Stored in arrays after loading.

Most of them seem to be good candidates to be turned into structs.

LigatureAttachTable, ComponentRecord, LookupTable, NameRecord ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll have a look.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gonna leave for now. I'd like to get this in and focus on potential performance issues after.

{
internal struct AttachPoint
{
public ushort[] PointIndices;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is filled but unused.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooh! I'll check this out!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah, this has something to do with font variations which we don't support yet.

/// Kerning is the contextual adjustment of inter-glyph spacing.
/// This property controls metric kerning, kerning that utilizes adjustment data contained in the font.
/// </summary>
public enum KerningMode
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

Copy link
Contributor

@brianpopow brianpopow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its time to get this huge PR merged! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
8 participants