Skip to content

Commit 6e09e3c

Browse files
committed
完成对接逻辑
1 parent 2d514b2 commit 6e09e3c

5 files changed

Lines changed: 105 additions & 95 deletions

File tree

src/MediaConverters/SkiaWmfRenderer/samples/sample/Program.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
skPaint.Color = SKColors.Black;
6262
skPaint.IsAntialias = true;
6363
var skTextBlob = SKTextBlob.Create("p",skFont);
64-
skCanvas.DrawText(skTextBlob, 50, 100, skPaint);
64+
//skCanvas.DrawText(skTextBlob, 50, 100, skPaint);
6565

6666
using (var buffer = new Buffer())
6767
{
@@ -93,9 +93,10 @@
9393

9494
if (tryGetGlyph)
9595
{
96-
var bytes = BitConverter.GetBytes((ushort)glyph);
96+
Span<byte> byteBuffer = stackalloc byte[sizeof(ushort)];
97+
BitConverter.TryWriteBytes(byteBuffer, (ushort)glyph);
9798

98-
skTextBlob = SKTextBlob.Create(bytes, SKTextEncoding.GlyphId, skFont);
99+
skTextBlob = SKTextBlob.Create(byteBuffer, SKTextEncoding.GlyphId, skFont);
99100
skCanvas.DrawText(skTextBlob, 100, 100, skPaint);
100101
}
101102

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System.Runtime.InteropServices;
2+
3+
using HarfBuzzSharp;
4+
5+
using SkiaSharp;
6+
7+
using Buffer = HarfBuzzSharp.Buffer;
8+
9+
namespace SkiaWmfRenderer.Rendering;
10+
11+
static class RenderTextHelper
12+
{
13+
public static void DrawText(this SKCanvas canvas, string text, float x, float y, WmfRenderStatus renderStatus)
14+
{
15+
using var buffer = new Buffer();
16+
17+
buffer.AddUtf16(text);
18+
buffer.GuessSegmentProperties();
19+
20+
var font = renderStatus.HarfBuzzFont;
21+
22+
font.Shape(buffer);
23+
24+
ReadOnlySpan<GlyphInfo> glyphInfoSpan = buffer.GetGlyphInfoSpan();
25+
Span<ushort> glyphsSpan = glyphInfoSpan.Length < 1024 ?
26+
stackalloc ushort[glyphInfoSpan.Length]
27+
: (Span<ushort>) new ushort[glyphInfoSpan.Length];
28+
29+
for (int i = 0; i < glyphInfoSpan.Length; i++)
30+
{
31+
var codepoint = glyphInfoSpan[i].Codepoint;
32+
if (font.TryGetGlyph(codepoint, out uint glyphId))
33+
{
34+
glyphsSpan[i] = (ushort) glyphId;
35+
}
36+
else
37+
{
38+
// 暂时不知道怎么处理
39+
glyphsSpan[i] = 0;
40+
}
41+
}
42+
43+
Span<byte> byteBuffer = MemoryMarshal.AsBytes(glyphsSpan);
44+
45+
using var skTextBlob = SKTextBlob.Create(byteBuffer, SKTextEncoding.GlyphId, renderStatus.SKFont);
46+
canvas.DrawText(skTextBlob, x, y, renderStatus.Paint);
47+
}
48+
}

src/MediaConverters/SkiaWmfRenderer/src/SkiaWmfRenderer/Rendering/WmfRenderStatus.cs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
using System.Text;
1+
using HarfBuzzSharp;
22

33
using Oxage.Wmf;
44

55
using SkiaSharp;
66

7+
using System.Runtime.InteropServices;
8+
using System.Text;
9+
710
namespace SkiaWmfRenderer.Rendering;
811

912
class WmfRenderStatus : IDisposable
@@ -54,19 +57,57 @@ public CharacterSet CurrentCharacterSet
5457

5558
public SKFont SKFont { get; } = new SKFont();
5659

60+
public HarfBuzzSharp.Font HarfBuzzFont
61+
{
62+
get
63+
{
64+
if (_harfBuzzFont is null)
65+
{
66+
_harfBuzzFace = new HarfBuzzSharp.Face(GetTable);
67+
68+
Blob? GetTable(Face f, Tag tag)
69+
{
70+
var skTypeface = SKFont.Typeface ?? SKTypeface.Default;
71+
72+
var size = skTypeface.GetTableSize(tag);
73+
var data = Marshal.AllocCoTaskMem(size);
74+
if (skTypeface.TryGetTableData(tag, 0, size, data))
75+
{
76+
return new Blob(data, size, MemoryMode.ReadOnly, () => Marshal.FreeCoTaskMem(data));
77+
}
78+
else
79+
{
80+
return null;
81+
}
82+
}
83+
84+
var font = new HarfBuzzSharp.Font(_harfBuzzFace);
85+
font.SetFunctionsOpenType();
86+
_harfBuzzFont = font;
87+
}
88+
return _harfBuzzFont;
89+
}
90+
}
91+
92+
private HarfBuzzSharp.Font? _harfBuzzFont;
93+
private HarfBuzzSharp.Face? _harfBuzzFace;
5794

5895
public void UpdateSkiaTextStatus(string text)
5996
{
6097
var skFont = SKFont;
6198
skFont.Size = CurrentFontSize;
6299

63100
skFont.Typeface?.Dispose();
101+
_harfBuzzFont?.Dispose();
102+
_harfBuzzFace?.Dispose();
103+
_harfBuzzFont = null;
104+
_harfBuzzFace = null;
64105

65106
SKTypeface? typeface;
66107
if (CurrentFontName == "Symbol")
67108
{
68109
var symbolFontFile = Path.Join(AppContext.BaseDirectory, "StandardSymbolsPS.ttf");
69-
symbolFontFile = Path.Join(AppContext.BaseDirectory, "symbol.ttf");
110+
//symbolFontFile = Path.Join(AppContext.BaseDirectory, "symbol.ttf");
70111
typeface = SKTypeface.FromFile(symbolFontFile);
71112
}
72113
else
@@ -84,7 +125,7 @@ public void UpdateSkiaTextStatus(string text)
84125
Paint.Style = SKPaintStyle.Fill;
85126
Paint.Color = CurrentTextColor;
86127

87-
Paint.Typeface = typeface;
128+
//Paint.Typeface = typeface;
88129
}
89130

90131
public void UpdateSkiaStrokeStatus()
@@ -110,5 +151,8 @@ public void Dispose()
110151
Paint.Dispose();
111152
SKFont.Typeface?.Dispose();
112153
SKFont.Dispose();
154+
155+
_harfBuzzFont?.Dispose();
156+
_harfBuzzFace?.Dispose();
113157
}
114158
}

src/MediaConverters/SkiaWmfRenderer/src/SkiaWmfRenderer/Rendering/WmfRenderer.cs

Lines changed: 2 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,7 @@ private static bool RenderRecord(SKCanvas canvas, WmfRenderStatus renderStatus,
415415

416416
if (extTextoutRecord.Dx is null)
417417
{
418-
canvas.DrawText(text, currentXOffset, renderStatus.CurrentY + extTextoutRecord.Y, renderStatus.SKFont,
419-
renderStatus.Paint);
418+
canvas.DrawText(text, currentXOffset, renderStatus.CurrentY + extTextoutRecord.Y, renderStatus);
420419
}
421420
else
422421
{
@@ -435,8 +434,7 @@ private static bool RenderRecord(SKCanvas canvas, WmfRenderStatus renderStatus,
435434
for (var textIndex = 0; textIndex < text.Length; textIndex++)
436435
{
437436
canvas.DrawText(text[textIndex].ToString(), currentXOffset,
438-
renderStatus.CurrentY + extTextoutRecord.Y, renderStatus.SKFont,
439-
renderStatus.Paint);
437+
renderStatus.CurrentY + extTextoutRecord.Y, renderStatus);
440438

441439
currentXOffset += extTextoutRecord.Dx[textIndex];
442440
}
@@ -452,91 +450,6 @@ private static bool RenderRecord(SKCanvas canvas, WmfRenderStatus renderStatus,
452450
{
453451
Debug.Fail("当前已经有 WmfExtTextoutRecord 类型了");
454452

455-
renderStatus.IsIncludeText = true;
456-
457-
// 关于字间距的规则:
458-
// 1. 如果两个 META_EXTTEXTOUT 相邻,中间没有 MoveTo 之类
459-
// 则第二个 META_EXTTEXTOUT 将需要使用前一个 META_EXTTEXTOUT 的 dx 末项
460-
// 2. 可选的 dx 是存放在字符串末尾的可选项,从文档 2.3.3.5 上可见 dx 是顶格写的,这就意味着这个值是一定对齐整数倍的。由于 dx 是放在数据末尾,可通过减法算出 dx 长度,即数据总长度减去所有已知字段的长度加上字符串长度,剩余的就是 dx 长度。如果计算返回的 dx 长度是奇数,则首个 byte 是需要跳过的,如此就能确保在 16bit 下的 wmf 格式里面,读取的 dx 是从整数倍开始读取
461-
// 参考 https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-exttextoutw
462-
// 测试 17 项
463-
var memoryStream = new MemoryStream(unknownRecord.Data);
464-
var binaryReader = new BinaryReader(memoryStream);
465-
var ty = binaryReader.ReadUInt16();
466-
var tx = binaryReader.ReadUInt16();
467-
var stringLength = binaryReader.ReadUInt16();
468-
var fwOpts = (ExtTextOutOptions)binaryReader.ReadUInt16();
469-
// Rectangle (8 bytes): An optional 8-byte Rect Object (section 2.2.2.18).) When either ETO_CLIPPED, ETO_OPAQUE, or both are specified, the rectangle defines the dimensions, in logical coordinates, used for clipping, opaquing, or both. When neither ETO_CLIPPED nor ETO_OPAQUE is specified, the coordinates in Rectangle are ignored.
470-
var st = 8; /*2byte of ty tx stringLength fwOpts*/
471-
if (fwOpts is ExtTextOutOptions.ETO_CLIPPED or ExtTextOutOptions.ETO_OPAQUE)
472-
{
473-
// 此时才有 Rectangle 的值
474-
binaryReader.ReadBytes(8);
475-
st += 8;
476-
}
477-
478-
st += stringLength;
479-
480-
// String (variable): A variable-length string that specifies the text to be drawn. The string does not need to be null-terminated, because StringLength specifies the length of the string. If the length is odd, an extra byte is placed after it so that the following member (optional Dx) is aligned on a 16-bit boundary. The string will be decoded based on the font object currently selected into the playback device context. If a font matching the font object’s specification is not found, the decoding is undefined. If a matching font is found that matches the charset specified in the font object, the string should be decoded with the codepages in the following table.
481-
var stringBuffer = binaryReader.ReadBytes(stringLength);
482-
var text = renderStatus.CurrentEncoding.GetString(stringBuffer);
483-
484-
// 是否包含了其他编码
485-
var isIncludeOtherEncoding = renderStatus.CurrentCharacterSet != CharacterSet.DEFAULT_CHARSET;
486-
renderStatus.IsIncludeOtherEncoding |=
487-
isIncludeOtherEncoding;
488-
489-
// Dx (variable): An optional array of 16-bit signed integers that indicate the distance between origins of adjacent character cells. For example, Dx[i] logical units separate the origins of character cell i and character cell i + 1. If this field is present, there MUST be the same number of values as there are characters in the string.
490-
//Debug.Assert(st == unknownRecord.RecordSize);
491-
var dxLength = unknownRecord.Data.Length - st;
492-
493-
renderStatus.UpdateSkiaTextStatus(text);
494-
495-
var currentXOffset = renderStatus.CurrentX + tx + renderStatus.LastDxOffset;
496-
497-
var isIncludeTextWithDx = renderStatus.LastDxOffset > 0;
498-
renderStatus.IsIncludeTextWithDx |=
499-
isIncludeTextWithDx;
500-
501-
if (dxLength == 0)
502-
{
503-
canvas.DrawText(text, currentXOffset, renderStatus.CurrentY + ty, renderStatus.SKFont,
504-
renderStatus.Paint);
505-
}
506-
else
507-
{
508-
// 如果这里计算出来不是偶数,则首个需要跳过。这是经过测试验证的。~~但没有相关说明内容。且跳过的 byte 是有内容的~~ String (variable): If the length is odd, an extra byte is placed after it so that the following member (optional Dx) is aligned on a 16-bit boundary.
509-
// 如果字符串的长度是奇数,则在字符串后面放置一个额外的字节,以便下一个成员(可选的 Dx)对齐到 16 位边界。
510-
if (dxLength > ((dxLength / sizeof(UInt16)) * sizeof(UInt16)))
511-
{
512-
// 读取掉这个额外的字节,以便 Dx 对齐到 16 位边界
513-
var r = binaryReader.ReadByte();
514-
_ = r;
515-
}
516-
517-
UInt16[] dxArray = new UInt16[dxLength / sizeof(UInt16)];
518-
for (var t = 0; t < dxArray.Length; t++)
519-
{
520-
dxArray[t] = binaryReader.ReadUInt16();
521-
}
522-
523-
if (dxArray.Length != text.Length)
524-
{
525-
return false;
526-
}
527-
528-
for (var textIndex = 0; textIndex < text.Length; textIndex++)
529-
{
530-
canvas.DrawText(text[textIndex].ToString(), currentXOffset,
531-
renderStatus.CurrentY + ty, renderStatus.SKFont,
532-
renderStatus.Paint);
533-
534-
currentXOffset += dxArray[textIndex];
535-
}
536-
537-
renderStatus.LastDxOffset = dxArray[^1];
538-
}
539-
540453
break;
541454
}
542455
}

src/MediaConverters/SkiaWmfRenderer/src/SkiaWmfRenderer/SkiaWmfRenderer.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
<PackageReference Include="dotnetCampus.LatestCSharpFeatures" Version="12.0.1" />
1717
<PackageReference Include="SkiaSharp" Version="3.119.0" />
1818
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.0" />
19+
20+
<PackageReference Include="HarfBuzzSharp" Version="8.3.1.1" />
21+
<PackageReference Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.1" />
22+
1923
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.7" />
2024
</ItemGroup>
2125

0 commit comments

Comments
 (0)