mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-15 19:54:52 +08:00
add writing methods to truetype tables #98
since we have verified the problem with the characters not appearing in acrobat reader isn't the checksum (other files also have invalid checksums but work) it seems likely the issue is with the os/2 table. this change moves the logic for writing out the cmap table, the format 6 cmap sub-table, truetype table headers and the os/2 table into the classes themselves. now we can write an os/2 table and we've tested that the output matches the input, we can overwrite the os/2 table in order to work out which of the os/2 errors is causing our font to be invalid. the writeable interface should be added to more and more parts of the codebase so that writing, editing and document creation become first class citizens rather than hardcoded additions. this change also adds the macroman (1,0) cmap subtable to edited fonts so that it is present for consumers which expect it.
This commit is contained in:
@@ -31,12 +31,14 @@
|
||||
var ulCharRange2 = (uint)data.ReadUnsignedInt();
|
||||
var ulCharRange3 = (uint)data.ReadUnsignedInt();
|
||||
var ulCharRange4 = (uint)data.ReadUnsignedInt();
|
||||
var vendorId = data.ReadByteArray(4);
|
||||
var vendorIdBytes = data.ReadByteArray(4);
|
||||
var selectionFlags = data.ReadUnsignedShort();
|
||||
var firstCharacterIndex = data.ReadUnsignedShort();
|
||||
var lastCharacterIndex = data.ReadUnsignedShort();
|
||||
var unicodeCharRange = new[] {ulCharRange1, ulCharRange2, ulCharRange3, ulCharRange4};
|
||||
|
||||
var vendorId = Encoding.ASCII.GetString(vendorIdBytes);
|
||||
|
||||
/*
|
||||
* Documentation for OS/2 version 0 in Apple’s TrueType Reference Manual stops at the usLastCharIndex field
|
||||
* and does not include the last five fields of the table as it was defined by Microsoft.
|
||||
@@ -59,7 +61,7 @@
|
||||
familyClass,
|
||||
panose,
|
||||
unicodeCharRange,
|
||||
Encoding.Unicode.GetString(vendorId),
|
||||
vendorId,
|
||||
selectionFlags,
|
||||
firstCharacterIndex,
|
||||
lastCharacterIndex);
|
||||
@@ -87,7 +89,7 @@
|
||||
familyClass,
|
||||
panose,
|
||||
unicodeCharRange,
|
||||
Encoding.Unicode.GetString(vendorId),
|
||||
vendorId,
|
||||
selectionFlags,
|
||||
firstCharacterIndex,
|
||||
lastCharacterIndex,
|
||||
@@ -116,7 +118,7 @@
|
||||
familyClass,
|
||||
panose,
|
||||
unicodeCharRange,
|
||||
Encoding.Unicode.GetString(vendorId),
|
||||
vendorId,
|
||||
selectionFlags,
|
||||
firstCharacterIndex,
|
||||
lastCharacterIndex,
|
||||
@@ -151,7 +153,7 @@
|
||||
familyClass,
|
||||
panose,
|
||||
unicodeCharRange,
|
||||
Encoding.Unicode.GetString(vendorId),
|
||||
vendorId,
|
||||
selectionFlags,
|
||||
firstCharacterIndex,
|
||||
lastCharacterIndex,
|
||||
@@ -186,7 +188,7 @@
|
||||
familyClass,
|
||||
panose,
|
||||
unicodeCharRange,
|
||||
Encoding.Unicode.GetString(vendorId),
|
||||
vendorId,
|
||||
selectionFlags,
|
||||
firstCharacterIndex,
|
||||
lastCharacterIndex,
|
||||
|
@@ -2,13 +2,18 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables.CMapSubTables
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using IO;
|
||||
using Util;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// A format 6 CMap sub-table which uses 2 bytes to map a contiguous range of character codes to glyph indices.
|
||||
/// </summary>
|
||||
internal class TrimmedTableMappingCMapTable : ICMapSubTable
|
||||
internal class TrimmedTableMappingCMapTable : ICMapSubTable, IWriteable
|
||||
{
|
||||
private const ushort Format = 6;
|
||||
private const ushort DefaultLanguageId = 0;
|
||||
|
||||
private readonly int entryCount;
|
||||
private readonly ushort[] glyphIndices;
|
||||
|
||||
@@ -69,5 +74,22 @@ namespace UglyToad.PdfPig.Fonts.TrueType.Tables.CMapSubTables
|
||||
|
||||
return new TrimmedTableMappingCMapTable(platformId, encodingId, firstCode, entryCount, glyphIndices);
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
stream.WriteUShort(Format);
|
||||
|
||||
var length = (ushort)((5 * sizeof(short)) + (sizeof(short) * glyphIndices.Length));
|
||||
|
||||
stream.WriteUShort(length);
|
||||
stream.WriteUShort(DefaultLanguageId);
|
||||
stream.WriteUShort(FirstCharacterCode);
|
||||
stream.WriteUShort(glyphIndices.Length);
|
||||
|
||||
for (var j = 0; j < glyphIndices.Length; j++)
|
||||
{
|
||||
stream.WriteUShort(glyphIndices[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,13 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using CMapSubTables;
|
||||
using IO;
|
||||
using Util;
|
||||
|
||||
internal class CMapTable : ITable
|
||||
internal class CMapTable : ITable, IWriteable
|
||||
{
|
||||
public IReadOnlyList<ICMapSubTable> SubTables { get; }
|
||||
|
||||
@@ -74,5 +78,53 @@
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
// Write cmap index.
|
||||
stream.WriteUShort(Version);
|
||||
stream.WriteUShort(SubTables.Count);
|
||||
|
||||
// Write cmap encoding subtable, an index of all subtables and store the offsets to correct once written.
|
||||
var subTableIndexOffsetPositions = new long[SubTables.Count];
|
||||
for (var i = 0; i < SubTables.Count; i++)
|
||||
{
|
||||
var subTable = SubTables[i];
|
||||
|
||||
stream.WriteUShort((ushort)subTable.PlatformId);
|
||||
stream.WriteUShort(subTable.EncodingId);
|
||||
|
||||
subTableIndexOffsetPositions[i] = stream.Position;
|
||||
stream.WriteUInt(0);
|
||||
}
|
||||
|
||||
// Write the full tables and store their actual offsets.
|
||||
var subTableActualPositions = new long[SubTables.Count];
|
||||
for (var i = 0; i < SubTables.Count; i++)
|
||||
{
|
||||
var subTable = SubTables[i];
|
||||
|
||||
if (!(subTable is IWriteable writeableSubTable))
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot write subtable of type: {subTable.GetType().Name}.");
|
||||
}
|
||||
|
||||
subTableActualPositions[i] = stream.Position;
|
||||
|
||||
writeableSubTable.Write(stream);
|
||||
}
|
||||
|
||||
// Return to the index to fix the offset values.
|
||||
var endAt = stream.Position;
|
||||
|
||||
for (var i = 0; i < subTableIndexOffsetPositions.Length; i++)
|
||||
{
|
||||
var actual = subTableActualPositions[i];
|
||||
stream.Seek(subTableIndexOffsetPositions[i], SeekOrigin.Begin);
|
||||
stream.WriteUInt(actual);
|
||||
}
|
||||
|
||||
stream.Seek(endAt, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Util;
|
||||
|
||||
/// <summary>
|
||||
/// Version 0 was defined in TrueType revision 1.5 and includes fields not in the Apple specification.
|
||||
@@ -68,5 +70,15 @@
|
||||
WindowsAscent = windowsAscent;
|
||||
WindowsDescent = windowsDescent;
|
||||
}
|
||||
|
||||
public override void Write(Stream stream)
|
||||
{
|
||||
base.Write(stream);
|
||||
stream.WriteShort(TypographicAscender);
|
||||
stream.WriteShort(TypographicDescender);
|
||||
stream.WriteShort(TypographicLineGap);
|
||||
stream.WriteUShort(WindowsAscent);
|
||||
stream.WriteUShort(WindowsDescent);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +1,15 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using IO;
|
||||
using Util;
|
||||
|
||||
/// <summary>
|
||||
/// The most basic format of the OS/2 table, excluding the fields not included in the Apple version of the specification.
|
||||
/// </summary>
|
||||
internal class Os2Table : ITable
|
||||
internal class Os2Table : ITable, IWriteable
|
||||
{
|
||||
public string Tag => TrueTypeHeaderTable.Os2;
|
||||
|
||||
@@ -97,7 +101,7 @@
|
||||
/// The PANOSE definition of 10 bytes defines various information about the
|
||||
/// font enabling matching fonts based on requirements. The meaning of each
|
||||
/// byte in the PANOSE definition depends on the preceding bytes. The first byte
|
||||
/// is the family type, Lating, Latin Hand Written, etc.
|
||||
/// is the family type, Latin, Latin Hand Written, etc.
|
||||
/// </summary>
|
||||
public IReadOnlyList<byte> Panose { get; }
|
||||
|
||||
@@ -174,5 +178,46 @@
|
||||
FirstCharacterIndex = firstCharacterIndex;
|
||||
LastCharacterIndex = lastCharacterIndex;
|
||||
}
|
||||
|
||||
public virtual void Write(Stream stream)
|
||||
{
|
||||
stream.WriteUShort(Version);
|
||||
stream.WriteShort(XAverageCharacterWidth);
|
||||
stream.WriteUShort(WeightClass);
|
||||
stream.WriteUShort(WidthClass);
|
||||
|
||||
stream.WriteShort(TypeFlags);
|
||||
|
||||
stream.WriteShort(YSubscriptXSize);
|
||||
stream.WriteShort(YSubscriptYSize);
|
||||
stream.WriteShort(YSubscriptXOffset);
|
||||
stream.WriteShort(YSubscriptYOffset);
|
||||
|
||||
stream.WriteShort(YSuperscriptXSize);
|
||||
stream.WriteShort(YSuperscriptYSize);
|
||||
stream.WriteShort(YSuperscriptXOffset);
|
||||
stream.WriteShort(YSuperscriptYOffset);
|
||||
|
||||
stream.WriteShort(YStrikeoutSize);
|
||||
stream.WriteShort(YStrikeoutPosition);
|
||||
|
||||
stream.WriteShort(FamilyClass);
|
||||
|
||||
stream.Write(Panose.ToArray(), 0, Panose.Count);
|
||||
|
||||
for (var i = 0; i < UnicodeRanges.Count; i++)
|
||||
{
|
||||
stream.WriteUInt(UnicodeRanges[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < VendorId.Length; i++)
|
||||
{
|
||||
stream.WriteByte((byte)VendorId[i]);
|
||||
}
|
||||
|
||||
stream.WriteUShort(FontSelectionFlags);
|
||||
stream.WriteUShort(FirstCharacterIndex);
|
||||
stream.WriteUShort(LastCharacterIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Util;
|
||||
|
||||
/// <summary>
|
||||
/// Version 1 was defined in TrueType revision 1.66. Version 1 has two additional fields beyond those in version 0.
|
||||
@@ -51,5 +53,12 @@
|
||||
CodePage1 = codePage1;
|
||||
CodePage2 = codePage2;
|
||||
}
|
||||
|
||||
public override void Write(Stream stream)
|
||||
{
|
||||
base.Write(stream);
|
||||
stream.WriteUInt(CodePage1);
|
||||
stream.WriteUInt(CodePage2);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Util;
|
||||
|
||||
/// <summary>
|
||||
/// Version 4 was defined in OpenType 1.5. Version 4 has the same fields as in version 2 and version 3.
|
||||
@@ -8,7 +10,6 @@
|
||||
/// </summary>
|
||||
internal class Os2Version2To4OpenTypeTable : Os2Version1Table
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This metric specifies the distance between the baseline and the approximate height of non-ascending lowercase letters.
|
||||
/// </summary>
|
||||
@@ -88,5 +89,15 @@
|
||||
BreakCharacter = breakCharacter;
|
||||
MaximumContext = maximumContext;
|
||||
}
|
||||
|
||||
public override void Write(Stream stream)
|
||||
{
|
||||
base.Write(stream);
|
||||
stream.WriteShort(XHeight);
|
||||
stream.WriteShort(CapHeight);
|
||||
stream.WriteUShort(DefaultCharacter);
|
||||
stream.WriteUShort(BreakCharacter);
|
||||
stream.WriteUShort(MaximumContext);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Util;
|
||||
|
||||
/// <summary>
|
||||
/// Version 5 was defined in OpenType 1.7.
|
||||
@@ -71,5 +73,12 @@
|
||||
LowerOpticalPointSize = lowerOpticalPointSize;
|
||||
UpperOpticalPointSize = upperOpticalPointSize;
|
||||
}
|
||||
|
||||
public override void Write(Stream stream)
|
||||
{
|
||||
base.Write(stream);
|
||||
stream.WriteUShort(LowerOpticalPointSize);
|
||||
stream.WriteUShort(UpperOpticalPointSize);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,12 +1,15 @@
|
||||
namespace UglyToad.PdfPig.Fonts.TrueType
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using IO;
|
||||
using Util;
|
||||
using Util.JetBrains.Annotations;
|
||||
|
||||
/// <summary>
|
||||
/// A table directory entry from the TrueType font file.
|
||||
/// </summary>
|
||||
internal struct TrueTypeHeaderTable
|
||||
internal struct TrueTypeHeaderTable : IWriteable
|
||||
{
|
||||
#region RequiredTableTags
|
||||
/// <summary>
|
||||
@@ -190,9 +193,29 @@
|
||||
Length = length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an empty header table with the non-tag values set to zero.
|
||||
/// </summary>
|
||||
public static TrueTypeHeaderTable GetEmptyHeaderTable(string tag)
|
||||
{
|
||||
return new TrueTypeHeaderTable(tag, 0, 0, 0);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Tag} {Offset} {Length} {CheckSum}";
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
for (var i = 0; i < Tag.Length; i++)
|
||||
{
|
||||
stream.WriteByte((byte)Tag[i]);
|
||||
}
|
||||
|
||||
stream.WriteUInt(CheckSum);
|
||||
stream.WriteUInt(Offset);
|
||||
stream.WriteUInt(Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
src/UglyToad.PdfPig/IO/IWriteable.cs
Normal file
15
src/UglyToad.PdfPig/IO/IWriteable.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace UglyToad.PdfPig.IO
|
||||
{
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a data structure can be written to an output stream.
|
||||
/// </summary>
|
||||
internal interface IWriteable
|
||||
{
|
||||
/// <summary>
|
||||
/// Write the data to the output stream.
|
||||
/// </summary>
|
||||
void Write(Stream stream);
|
||||
}
|
||||
}
|
45
src/UglyToad.PdfPig/Util/WritingExtensions.cs
Normal file
45
src/UglyToad.PdfPig/Util/WritingExtensions.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
namespace UglyToad.PdfPig.Util
|
||||
{
|
||||
using System.IO;
|
||||
|
||||
internal static class WritingExtensions
|
||||
{
|
||||
public static void WriteUInt(this Stream stream, long value) => WriteUInt(stream, (uint) value);
|
||||
public static void WriteUInt(this Stream stream, uint value)
|
||||
{
|
||||
var buffer = new[]
|
||||
{
|
||||
(byte) (value >> 24),
|
||||
(byte) (value >> 16),
|
||||
(byte) (value >> 8),
|
||||
(byte) value
|
||||
};
|
||||
|
||||
stream.Write(buffer, 0, 4);
|
||||
}
|
||||
|
||||
public static void WriteUShort(this Stream stream, int value) => WriteUShort(stream, (ushort) value);
|
||||
public static void WriteUShort(this Stream stream, ushort value)
|
||||
{
|
||||
var buffer = new[]
|
||||
{
|
||||
(byte) (value >> 8),
|
||||
(byte) value
|
||||
};
|
||||
|
||||
stream.Write(buffer, 0, 2);
|
||||
}
|
||||
|
||||
public static void WriteShort(this Stream stream, ushort value) => WriteShort(stream, (short)value);
|
||||
public static void WriteShort(this Stream stream, short value)
|
||||
{
|
||||
var buffer = new[]
|
||||
{
|
||||
(byte) (value >> 8),
|
||||
(byte) value
|
||||
};
|
||||
|
||||
stream.Write(buffer, 0, 2);
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,6 +8,9 @@
|
||||
using IO;
|
||||
using PdfPig.Fonts.TrueType;
|
||||
using PdfPig.Fonts.TrueType.Parser;
|
||||
using PdfPig.Fonts.TrueType.Tables;
|
||||
using PdfPig.Fonts.TrueType.Tables.CMapSubTables;
|
||||
using Util;
|
||||
|
||||
internal static class TrueTypeCMapReplacer
|
||||
{
|
||||
@@ -35,7 +38,7 @@
|
||||
|
||||
var inputTableHeaders = new Dictionary<string, InputHeader>(StringComparer.OrdinalIgnoreCase);
|
||||
var outputTableHeaders = new Dictionary<string, TrueTypeHeaderTable>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
|
||||
var fileChecksumOffset = SizeOfTag;
|
||||
|
||||
byte[] result;
|
||||
@@ -138,12 +141,12 @@
|
||||
$"did not match header length {inputLength} for table {inputHeader.Key}.");
|
||||
}
|
||||
|
||||
WriteUInt(stream, outputHeader.Offset);
|
||||
stream.WriteUInt(outputHeader.Offset);
|
||||
|
||||
if (isCmap)
|
||||
{
|
||||
// Also overwrite length.
|
||||
WriteUInt(stream, outputHeader.Length);
|
||||
stream.WriteUInt(outputHeader.Length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,14 +157,14 @@
|
||||
}
|
||||
|
||||
var inputBytes = new ByteArrayInputBytes(result);
|
||||
|
||||
|
||||
// Overwrite checksum values per table.
|
||||
foreach (var inputHeader in inputTableHeaders)
|
||||
{
|
||||
var outputHeader = outputTableHeaders[inputHeader.Key];
|
||||
|
||||
var headerOffset = inputHeader.Value.OffsetInInput;
|
||||
|
||||
|
||||
var newChecksum = TrueTypeChecksumCalculator.Calculate(inputBytes, outputHeader);
|
||||
|
||||
// Overwrite the checksum value.
|
||||
@@ -178,7 +181,7 @@
|
||||
|
||||
// Store the result in checksum adjustment.
|
||||
WriteUInt(result, checksumAdjustmentLocation, checksumAdjustment);
|
||||
|
||||
|
||||
var canParse = new TrueTypeFontParser().Parse(new TrueTypeDataBytes(new ByteArrayInputBytes(result)));
|
||||
|
||||
return result;
|
||||
@@ -197,30 +200,6 @@
|
||||
+ (buffer[location + 3] << 0));
|
||||
}
|
||||
|
||||
private static void WriteUInt(Stream stream, uint value)
|
||||
{
|
||||
var buffer = new[]
|
||||
{
|
||||
(byte) (value >> 24),
|
||||
(byte) (value >> 16),
|
||||
(byte) (value >> 8),
|
||||
(byte) value
|
||||
};
|
||||
|
||||
stream.Write(buffer, 0, 4);
|
||||
}
|
||||
|
||||
private static void WriteUShort(Stream stream, ushort value)
|
||||
{
|
||||
var buffer = new[]
|
||||
{
|
||||
(byte) (value >> 8),
|
||||
(byte) value
|
||||
};
|
||||
|
||||
stream.Write(buffer, 0, 2);
|
||||
}
|
||||
|
||||
private static void WriteUInt(byte[] array, uint offset, uint value)
|
||||
{
|
||||
array[offset] = (byte)(value >> 24);
|
||||
@@ -273,42 +252,23 @@
|
||||
{
|
||||
// We generate a format 6 sub-table.
|
||||
const ushort cmapVersion = 0;
|
||||
const ushort numberOfSubtables = 1;
|
||||
const ushort platformId = 3;
|
||||
const ushort encodingId = 0;
|
||||
const ushort format = 6;
|
||||
const ushort languageId = 0;
|
||||
|
||||
var glyphIndices = MapNewEncodingToGlyphIndexArray(font, newEncoding);
|
||||
|
||||
using (var memoryStream = new MemoryStream())
|
||||
|
||||
var cmapTable = new CMapTable(cmapVersion, new TrueTypeHeaderTable(CMapTag, 0, 0, 0), new[]
|
||||
{
|
||||
// Write cmap table header.
|
||||
WriteUShort(memoryStream, cmapVersion);
|
||||
WriteUShort(memoryStream, numberOfSubtables);
|
||||
new TrimmedTableMappingCMapTable(TrueTypeCMapPlatform.Windows, encodingId, 0, glyphIndices.Length, glyphIndices),
|
||||
new TrimmedTableMappingCMapTable(TrueTypeCMapPlatform.Macintosh, encodingId, 0, glyphIndices.Length, glyphIndices)
|
||||
});
|
||||
|
||||
// Write sub-table index.
|
||||
WriteUShort(memoryStream, platformId);
|
||||
WriteUShort(memoryStream, encodingId);
|
||||
WriteUInt(memoryStream, (uint)(memoryStream.Position + SizeOfInt));
|
||||
|
||||
// Write format 6 sub-table.
|
||||
WriteUShort(memoryStream, format);
|
||||
var length = (ushort)((5 * SizeOfShort) + (SizeOfShort * glyphIndices.Length));
|
||||
WriteUShort(memoryStream, length);
|
||||
WriteUShort(memoryStream, languageId);
|
||||
WriteUShort(memoryStream, 0);
|
||||
WriteUShort(memoryStream, (ushort)glyphIndices.Length);
|
||||
|
||||
for (var j = 0; j < glyphIndices.Length; j++)
|
||||
{
|
||||
WriteUShort(memoryStream, glyphIndices[j]);
|
||||
}
|
||||
|
||||
return memoryStream.ToArray();
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
cmapTable.Write(stream);
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static ushort[] MapNewEncodingToGlyphIndexArray(TrueTypeFontProgram font, IReadOnlyDictionary<char, byte> newEncoding)
|
||||
{
|
||||
var mappingTable = font.WindowsUnicodeCMap ?? font.WindowsSymbolCMap;
|
||||
|
Reference in New Issue
Block a user