字符串

时间:2016-10-22 14:55:30

标签: arrays string memory byte delphi-xe7

对于项目,我需要读取TrueType字体文件(.ttf)中的名称。我写了一个代码来实现这一点,从c ++示例中得到启发。这是代码:

TWByteArray     = array of Byte;
TWAnsiCharArray = array of AnsiChar;

...

//---------------------------------------------------------------------------
class function TWStringHelper.ByteToStr(const bytes: TWByteArray): string;
begin
    SetLength(Result, Length(bytes));

    if Length(Result) > 0 then
        Move(bytes[0], Result[1], Length(bytes));
end;
//---------------------------------------------------------------------------
class function TWStringHelper.UniStrToByte(const str: UnicodeString): TWByteArray;
begin
    SetLength(Result, Length(str) * SizeOf(WideChar));

    if (Length(Result) > 0) then
        Move(str[1], Result[0], Length(Result));
end;
//---------------------------------------------------------------------------
class function TWStringHelper.BytesToUniStr(const bytes: TWByteArray): UnicodeString;
begin
    SetLength(Result, Length(bytes) div SizeOf(WideChar));

    if Length(Result) > 0 then
        Move(bytes[0], Result[1], Length(bytes));
end;
//---------------------------------------------------------------------------

...        

//---------------------------------------------------------------------------
class function TWControlFont.SwapWord(value: Word): Word;
begin
    Result := MakeWord(HiByte(value), LoByte(value));
end;
//---------------------------------------------------------------------------
class function TWControlFont.SwapLong(value: LongInt): LongInt;
begin
    Result := MakeLong(SwapWord(HiWord(value)), SwapWord(LoWord(value)));
end;
//---------------------------------------------------------------------------
class function TWControlFont.GetFontNameFromFile(const fileName: UnicodeString): UnicodeString;
var
    pFile:       TFileStream;
    offsetTable: ITTFOffsetTable;
    dirTable:    ITTFDirectoryTable;
    nameHeader:  ITTFNameTableHeader;
    nameRecord:  ITTFNameRecord;
    nameBuffer:  TWByteArray;//TWAnsiCharArray;
    i:           USHORT;
    found:       Boolean;

    test2: string;
    test3: UnicodeString;
    test: Integer;

    const name:  array [0..3] of Byte = (Ord('n'), Ord('a'), Ord('m'), Ord('e'));
begin
    // open font file
    pFile := TFileStream.Create(fileName, fmOpenRead);

    // succeeded?
    if (not Assigned(pFile)) then
        Exit;

    try
        pFile.Seek(0, soFromBeginning);

        // read TTF offset table
        if (pFile.Read(offsetTable, SizeOf(ITTFOffsetTable)) <> SizeOf(ITTFOffsetTable)) then
            Exit;

        offsetTable.m_NumOfTables  := SwapWord(offsetTable.m_NumOfTables);
        offsetTable.m_MajorVersion := SwapWord(offsetTable.m_MajorVersion);
        offsetTable.m_MinorVersion := SwapWord(offsetTable.m_MinorVersion);

        // is truetype font and version is 1.0?
        if ((offsetTable.m_MajorVersion <> 1) or (offsetTable.m_MinorVersion <> 0)) then
            Exit;

        found := False;

        // iterate through file tables
        if (offsetTable.m_NumOfTables > 0) then
            for i := 0 to offsetTable.m_NumOfTables - 1 do
            begin
                // read table
                if (pFile.Read(dirTable, SizeOf(ITTFDirectoryTable)) <> SizeOf(ITTFDirectoryTable)) then
                    Exit;

                // found name table?
                if (CompareMem(@dirTable.m_Tag, @name, 4) = True) then
                begin
                    found             := True;
                    dirTable.m_Length := SwapLong(dirTable.m_Length);
                    dirTable.m_Offset := SwapLong(dirTable.m_Offset);
                    break;
                end;
            end;

        // found name table?
        if (not found) then
            Exit;

        // seek to name location
        pFile.Position := dirTable.m_Offset;

        // read name table header
        if (pFile.Read(nameHeader, SizeOf(ITTFNameTableHeader)) <> SizeOf(ITTFNameTableHeader)) then
            Exit;

        nameHeader.m_NRCount       := SwapWord(nameHeader.m_NRCount);
        nameHeader.m_StorageOffset := SwapWord(nameHeader.m_StorageOffset);

        // iterate through name records
        if (nameHeader.m_NRCount > 0) then
            for i := 0 to nameHeader.m_NRCount - 1 do
            begin
                // read name record
                if (pFile.Read(nameRecord, SizeOf(ITTFNameRecord)) <> SizeOf(ITTFNameRecord)) then
                    Exit;

                nameRecord.m_NameID := SwapWord(nameRecord.m_NameID);

                // found font name?
                if (nameRecord.m_NameID = 1) then
                begin
                    // get font name length and offset
                    nameRecord.m_StringLength := SwapWord(nameRecord.m_StringLength);
                    nameRecord.m_StringOffset := SwapWord(nameRecord.m_StringOffset);

                    if (nameRecord.m_StringLength = 0) then
                        continue;

                    // calculate and seek to font name offset
                    pFile.Position := dirTable.m_Offset + nameRecord.m_StringOffset + nameHeader.m_StorageOffset;

                    try
                        SetLength(nameBuffer, nameRecord.m_StringLength + 1);
                        //REM FillChar(nameBuffer[0], nameRecord.m_StringLength + 1, $0);

                        // read font name from file
                        if (pFile.Read(nameBuffer[0], nameRecord.m_StringLength)
                            <> nameRecord.m_StringLength)
                        then
                            Exit;

                        nameBuffer[nameRecord.m_StringLength] := $0;

                        //OutputDebugString(PChar(nameBuffer));
                        //TWMemoryHelper.SwapBytes(nameBuffer[0], nameRecord.m_StringLength);
                        //OutputDebugString(PChar(nameBuffer));

                        //test := StringElementSize(RawByteString(@nameBuffer[0]));
                        //Result := TWStringHelper.BytesToUniStr(nameBuffer);
                        //Result := UnicodeString(AnsiString(TWStringHelper.ByteToStr(nameBuffer)));
                        //REM Result := UnicodeString(nameBuffer);
                        test2 := TWStringHelper.ByteToStr(nameBuffer);
                        OutputDebugStringA(PAnsiChar(test2));
                        test3 := UnicodeString(PAnsiChar(test2));
                        OutputDebugStringW(PWideChar(test3));    
                        Result := test3;
                        OutputDebugStringW(PWideChar(test3));

                    finally
                        SetLength(nameBuffer, 0);
                    end;

                    break;
                end;
            end;
    finally
        pFile.Free;
    end;
end;
//---------------------------------------------------------------------------

此代码适用于GetFontNameFromFile()函数的最后一部分。事情开始变得复杂起来。实际上,我无法以正确的方式将nameBuffer字节数组转换为字符串。

我遇到的第一个问题是nameBuffer可能是一个简单的ASCII字符串,或UTF16字符串,依赖于文件(我尝试使用FireFox中提供的emoji.ttf,它返回一个ASCII字符串,以及来自的Tahoma.ttf)我的Win安装,返回UTF16字符串)。我需要一种方法来确定,并且我不知道VCL中是否有函数或类来执行此操作。

第二个问题是转换本身。上面的代码或多或少有效,但我觉得这不是一个正确的解决方案。当我尝试直接从nameBuffer转换为UnicodeString时,我遇到了一些奇怪的崩溃。如果我尝试将nameBuffer转换为AnsiString,转换似乎成功,但是像UnicodeString(AnsiString(nameBuffer))这样的转换失败。

代码似乎充满了内存问题。由于我是Delphi的新手,我对内存使用情况不太满意。例如,我怀疑在激活

时字节数组有几个问题
FillChar(nameBuffer[0], nameRecord.m_StringLength + 1, $0);

线。

所以任何人都可以分析这些代码并指出我做错了什么?

提前致谢, 此致

0 个答案:

没有答案