[CoreText] CTParagraphStyle uses the incorrect float return type. Fixes bug 54148. (#1975)

* [CoreText] Fix bug 54148 - CoreText.CTParagraphStyle does not pick up settings from CTParagraphStyleSettings

https://bugzilla.xamarin.com/show_bug.cgi?id=54148

CTParagraphStyle float properties have the incorrect float return type,
the headers state this API's returns CGFloats (aka nfloat) instead of floats
this used to work ok fetching them due to there is no difference in size
for 32 bits devices but once 64 bit devices appeared the API began to fail

The actual method that fetches the values `CTParagraphStyleGetValueForSpecifier`
asks for the size of the returned data and we used to give the size of a float
which is incorrect in 64 bits devices and the API call just correctly returned
false because it could not write back the value to us.

Added tests for the full properties available on CTParagraphStyle

* Add comment about the weird Dispose method implementation in CreateFromSettings
This commit is contained in:
Alex Soto 2017-04-10 01:24:25 -05:00 коммит произвёл Rolf Bjarne Kvinge
Родитель 2a85ec1674
Коммит 3fe0d2010a
3 изменённых файлов: 186 добавлений и 25 удалений

Просмотреть файл

@ -375,7 +375,9 @@ namespace XamCore.CoreText {
} }
handle = CTParagraphStyleCreate (settings, settings.Length); handle = CTParagraphStyleCreate (settings, settings.Length);
} }
// Yes this weird Dispose implementation is correct, this bugzilla
// comment explains more about it. TL;DR: check CTParagraphStyleSpecifierIntPtrsValue
// https://bugzilla.xamarin.com/show_bug.cgi?id=54148#c4
i = 0; i = 0;
foreach (var e in specifiers) { foreach (var e in specifiers) {
e.Dispose (values, i); e.Dispose (values, i);
@ -431,52 +433,122 @@ namespace XamCore.CoreText {
get {return (CTWritingDirection) GetByteValue (CTParagraphStyleSpecifier.BaseWritingDirection);} get {return (CTWritingDirection) GetByteValue (CTParagraphStyleSpecifier.BaseWritingDirection);}
} }
public float FirstLineHeadIndent { public
get {return GetFloatValue (CTParagraphStyleSpecifier.FirstLineHeadIndent);} #if XAMCORE_4_0
nfloat
#else
float
#endif
FirstLineHeadIndent {
get { return GetFloatValue (CTParagraphStyleSpecifier.FirstLineHeadIndent); }
} }
unsafe float GetFloatValue (CTParagraphStyleSpecifier spec) unsafe
#if XAMCORE_4_0
nfloat
#else
float
#endif
GetFloatValue (CTParagraphStyleSpecifier spec)
{ {
float value; nfloat value;
if (!CTParagraphStyleGetValueForSpecifier (handle, spec, sizeof (float), &value)) if (!CTParagraphStyleGetValueForSpecifier (handle, spec, (nuint) sizeof (nfloat), &value))
throw new InvalidOperationException ("Unable to get property value."); throw new InvalidOperationException ("Unable to get property value.");
return value; return
#if !XAMCORE_4_0
(float)
#endif
value;
} }
public float HeadIndent { public
get {return GetFloatValue (CTParagraphStyleSpecifier.HeadIndent);} #if XAMCORE_4_0
nfloat
#else
float
#endif
HeadIndent {
get { return GetFloatValue (CTParagraphStyleSpecifier.HeadIndent); }
} }
public float TailIndent { public
get {return GetFloatValue (CTParagraphStyleSpecifier.TailIndent);} #if XAMCORE_4_0
nfloat
#else
float
#endif
TailIndent {
get { return GetFloatValue (CTParagraphStyleSpecifier.TailIndent); }
} }
public float DefaultTabInterval { public
get {return GetFloatValue (CTParagraphStyleSpecifier.DefaultTabInterval);} #if XAMCORE_4_0
nfloat
#else
float
#endif
DefaultTabInterval {
get { return GetFloatValue (CTParagraphStyleSpecifier.DefaultTabInterval); }
} }
public float LineHeightMultiple { public
get {return GetFloatValue (CTParagraphStyleSpecifier.LineHeightMultiple);} #if XAMCORE_4_0
nfloat
#else
float
#endif
LineHeightMultiple {
get { return GetFloatValue (CTParagraphStyleSpecifier.LineHeightMultiple); }
} }
public float MaximumLineHeight { public
get {return GetFloatValue (CTParagraphStyleSpecifier.MaximumLineHeight);} #if XAMCORE_4_0
nfloat
#else
float
#endif
MaximumLineHeight {
get { return GetFloatValue (CTParagraphStyleSpecifier.MaximumLineHeight); }
} }
public float MinimumLineHeight { public
get {return GetFloatValue (CTParagraphStyleSpecifier.MinimumLineHeight);} #if XAMCORE_4_0
nfloat
#else
float
#endif
MinimumLineHeight {
get { return GetFloatValue (CTParagraphStyleSpecifier.MinimumLineHeight); }
} }
public float LineSpacing { public
get {return GetFloatValue (CTParagraphStyleSpecifier.LineSpacing);} #if XAMCORE_4_0
nfloat
#else
float
#endif
LineSpacing {
get { return GetFloatValue (CTParagraphStyleSpecifier.LineSpacing); }
} }
public float ParagraphSpacing { public
get {return GetFloatValue (CTParagraphStyleSpecifier.ParagraphSpacing);} #if XAMCORE_4_0
nfloat
#else
float
#endif
ParagraphSpacing {
get { return GetFloatValue (CTParagraphStyleSpecifier.ParagraphSpacing); }
} }
public float ParagraphSpacingBefore { public
get {return GetFloatValue (CTParagraphStyleSpecifier.ParagraphSpacingBefore);} #if XAMCORE_4_0
nfloat
#else
float
#endif
ParagraphSpacingBefore {
get { return GetFloatValue (CTParagraphStyleSpecifier.ParagraphSpacingBefore); }
} }
#endregion #endregion
} }

Просмотреть файл

@ -0,0 +1,88 @@
//
// Unit tests for CTParagraphStyle
//
// Authors:
// Alex Soto <alexsoto@microsoft.com>
//
// Copyright 2017 Xamarin Inc. All rights reserved.
//
#if !__WATCHOS__
using System;
using NUnit.Framework;
using System.Linq;
#if XAMCORE_2_0
using Foundation;
using CoreText;
#else
using MonoTouch.CoreText;
using MonoTouch.Foundation;
#endif
#if XAMCORE_2_0
using RectangleF = CoreGraphics.CGRect;
using SizeF = CoreGraphics.CGSize;
using PointF = CoreGraphics.CGPoint;
#else
using nfloat = global::System.Single;
using nint = global::System.Int32;
using nuint = global::System.UInt32;
#endif
namespace MonoTouchFixtures.CoreText {
[TestFixture]
[Preserve (AllMembers = true)]
public class CTParagraphStyleTests {
[Test]
public void StylePropertiesTest ()
{
var settings = new CTParagraphStyleSettings () {
TailIndent = 5,
ParagraphSpacingBefore = 5,
ParagraphSpacing = 5,
LineSpacing = 5,
MinimumLineHeight = 5,
MaximumLineHeight = 5,
LineHeightMultiple = 5,
DefaultTabInterval = 5,
HeadIndent = 5,
FirstLineHeadIndent = 5,
LineBreakMode = CTLineBreakMode.TruncatingHead,
BaseWritingDirection = CTWritingDirection.Natural,
Alignment = CTTextAlignment.Justified,
TabStops = new [] {
new CTTextTab (CTTextAlignment.Justified, 2),
new CTTextTab (CTTextAlignment.Natural, 1)
}
};
var style = new CTParagraphStyle (settings);
Assert.DoesNotThrow (() => {
Assert.AreEqual (settings.TailIndent, style.TailIndent, "TailIndent");
Assert.AreEqual (settings.ParagraphSpacingBefore, style.ParagraphSpacingBefore, "ParagraphSpacingBefore");
Assert.AreEqual (settings.ParagraphSpacing, style.ParagraphSpacing, "ParagraphSpacing");
Assert.AreEqual (settings.LineSpacing, style.LineSpacing, "LineSpacing");
Assert.AreEqual (settings.MinimumLineHeight, style.MinimumLineHeight, "MinimumLineHeight");
Assert.AreEqual (settings.MaximumLineHeight, style.MaximumLineHeight, "MaximumLineHeight");
Assert.AreEqual (settings.LineHeightMultiple, style.LineHeightMultiple, "LineHeightMultiple");
Assert.AreEqual (settings.DefaultTabInterval, style.DefaultTabInterval, "DefaultTabInterval");
Assert.AreEqual (settings.HeadIndent, style.HeadIndent, "HeadIndent");
Assert.AreEqual (settings.FirstLineHeadIndent, style.FirstLineHeadIndent, "FirstLineHeadIndent");
Assert.AreEqual (settings.LineBreakMode, style.LineBreakMode, "LineBreakMode");
Assert.AreEqual (settings.BaseWritingDirection, style.BaseWritingDirection, "LineBreakMode");
Assert.AreEqual (settings.Alignment, style.Alignment, "Alignment");
var styleTabStops = style.GetTabStops ();
Assert.AreEqual (settings.TabStops.Count (), styleTabStops.Length, "TabStops");
Assert.True (styleTabStops.Any (t => t.Location == 2 && t.TextAlignment == CTTextAlignment.Justified));
Assert.True (styleTabStops.Any (t => t.Location == 1 && t.TextAlignment == CTTextAlignment.Natural));
});
}
}
}
#endif

Просмотреть файл

@ -649,6 +649,7 @@
<Compile Include="AVFoundation\PlayerItemVideoOutputTest.cs" /> <Compile Include="AVFoundation\PlayerItemVideoOutputTest.cs" />
<Compile Include="mono\ConfigTest.cs" /> <Compile Include="mono\ConfigTest.cs" />
<Compile Include="OpenGLES\EAGLContext.cs" /> <Compile Include="OpenGLES\EAGLContext.cs" />
<Compile Include="CoreText\CTParagraphStyleTests.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<ItemGroup> <ItemGroup>