{******************************************} { } { FastReport FMX v2.0 } { Font parser } { } { Copyright (c) 1998-2013 } { by Aleksey Mandrykin, } { Fast Reports Inc. } { } {******************************************} unit FMX.frxCmapTableClass; interface uses System.SysUtils, FMX.TTFHelpers, FMX.frxTrueTypeTable; type // Nested Types EncodingFormats = (ByteEncoding=0, HighByteMapping=2, SegmentMapping=4, TrimmedTable=6); TSmallintArray = array of Smallint; TWordArray = array of Word; TSegmentMapping = packed record public segCountX2: Word; public searchRange: Word; public entrySelector: Word; public rangeShift: Word; end; Table_CMAP = packed record public TableVersion: Word; public NumSubTables: Word; end; Table_Encode = packed record public Format: Word; Length: Word; Version: Word; end; Table_ByteEncodingSubTable = packed record public Format: Word; Length: Word; language: Word; GlyphIndexArray: Array[0 .. 255] of Byte; end; Table_SUBMAP = packed record public Platform: Word; public EncodingID: Word; public TableOffset: Cardinal; end; TCmapSubTableTable = class(TObject); TCmapByteEncodingSubTable = class(TCmapSubTableTable) private FGlyphIndexArray: Array[0 .. 255] of Byte; public function GetGlyphIndex(ch: Byte): Word; end; TMappingTable = packed record public EndCount: TWordArray; GlyphIndexArray: TWordArray; idDelta: TSmallintArray; idRangeOffset: TWordArray; StartCount: TWordArray; segment_count: Integer; end; CmapTableClass = class(TrueTypeTable) // Fields private FByteEncodingSubTable: TCmapByteEncodingSubTable; FWinCmap: TMappingTable; FMacCmap: TMappingTable; // Methods function InternalGlyphIndex(CMapTable:TMappingTable; ch: Word): Word; public constructor Create(src: TrueTypeTable); destructor Destroy; override; public function GetGlyphIndex(ch: Word): Word; private function LoadCmapSegment(segment_ptr: Pointer; segment_count: Integer): TWordArray; public procedure LoadCmapTable(font: Pointer); strict private function LoadSignedCmapSegment(segment_ptr: Pointer; segment_count: Integer): TSmallintArray; end; implementation // Methods constructor CmapTableClass.Create(src: TrueTypeTable); begin inherited Create(src); FWinCmap.segment_count := 0; FMacCmap.segment_count := 0; FWinCmap.StartCount := nil; FMacCmap.StartCount := nil; FWinCmap.EndCount := nil; FMacCmap.EndCount := nil; end; destructor CmapTableClass.Destroy; begin FreeAndNil(FByteEncodingSubTable); inherited; end; function CmapTableClass.GetGlyphIndex(ch: Word): Word; begin {$IFDEF MACOS} Result := InternalGlyphIndex(FMacCmap, ch); if Result = 0 then Result := InternalGlyphIndex(FWinCmap, ch); {$ELSE} Result := InternalGlyphIndex(FWinCmap, ch); if Result = 0 then Result := InternalGlyphIndex(FMacCmap, ch); {$ENDIF} if Assigned(FByteEncodingSubTable) and (Result = 0) then Result := FByteEncodingSubTable.GetGlyphIndex(Byte(ch)); end; function CmapTableClass.InternalGlyphIndex(CMapTable: TMappingTable; ch: Word): Word; var i,j: Integer; begin Result := 0; i := 0; while ((i < CMapTable.segment_count)) do begin if (CMapTable.EndCount[i] >= ch) then begin if (CMapTable.StartCount[i] > ch) then exit; if (CMapTable.idRangeOffset[i] = 0) then begin Result := Word((ch + CMapTable.idDelta[i]) mod $10000); exit end; j := Word(((CMapTable.idRangeOffset[i] div 2) + (ch - CMapTable.StartCount[i])) - (CMapTable.segment_count - i)); Result := CMapTable.GlyphIndexArray[j]; exit; end; inc(i) end; end; function CmapTableClass.LoadCmapSegment(segment_ptr: Pointer; segment_count: Integer): TWordArray; var i: Integer; ptr: ^Word; begin ptr := segment_ptr; SetLength(Result, segment_count); i := 0; while ((i < segment_count)) do begin Result[i] := TTF_Helpers.SwapUInt16(ptr^); Inc(i); Inc(ptr); end; end; procedure CmapTableClass.LoadCmapTable(font: Pointer); var encode_ptr: ^Table_Encode; encode: Table_Encode; cmap: ^Table_CMAP; j, i, subtables_count: Integer; submap: Table_SUBMAP; submap_ptr: ^Table_SUBMAP; ByteEncodingSubTable: ^Table_ByteEncodingSubTable; segment: TSegmentMapping; segment_ptr: ^TSegmentMapping; index_array_size: Cardinal; CurCmap: ^TMappingTable; begin cmap := TTF_Helpers.Increment( font, self.entry.offset ); subtables_count := TTF_Helpers.SwapUInt16(cmap.NumSubTables); submap_ptr := TTF_Helpers.Increment(cmap, SizeOf(Table_CMAP)); j := 0; while ((j < subtables_count)) do begin // submap := submap_ptr; submap.Platform := TTF_Helpers.SwapUInt16(submap_ptr.Platform); submap.EncodingID := TTF_Helpers.SwapUInt16(submap_ptr.EncodingID); submap.TableOffset := TTF_Helpers.SwapUInt32(submap_ptr.TableOffset); submap_ptr := TTF_Helpers.Increment(submap_ptr, SizeOf(Table_SUBMAP)); if ((submap.Platform in [1, 3]) and (submap.EncodingID in [0, 1])) then begin case submap.Platform of 1: CurCmap := @FMacCmap; else CurCmap := @FWinCmap; end; encode_ptr := TTF_Helpers.Increment(cmap, submap.TableOffset); // encode := encode_ptr; encode.Format := TTF_Helpers.SwapUInt16(encode_ptr.Format); encode.Length := TTF_Helpers.SwapUInt16(encode_ptr.Length); encode.Version := TTF_Helpers.SwapUInt16(encode_ptr.Version); case encode.Format of 0: begin if FByteEncodingSubTable = nil then FByteEncodingSubTable := TCmapByteEncodingSubTable.Create; ByteEncodingSubTable := (Pointer(encode_ptr)); for i := 0 to 255 do FByteEncodingSubTable.FGlyphIndexArray[i] := ByteEncodingSubTable.GlyphIndexArray[i]; end; 4: begin segment_ptr := TTF_Helpers.Increment(encode_ptr, SizeOf(Table_Encode)); // segment := encode_ptr; segment.segCountX2 := TTF_Helpers.SwapUInt16(segment_ptr.segCountX2); segment.searchRange := TTF_Helpers.SwapUInt16(segment_ptr.searchRange); segment.entrySelector := TTF_Helpers.SwapUInt16(segment_ptr.entrySelector); segment.rangeShift := TTF_Helpers.SwapUInt16(segment_ptr.rangeShift); CurCmap^.segment_count := (segment.segCountX2 div 2); encode_ptr := TTF_Helpers.Increment(segment_ptr, SizeOf(TSegmentMapping)); CurCmap^.endCount := self.LoadCmapSegment(encode_ptr, CurCmap^.segment_count); encode_ptr := TTF_Helpers.Increment(encode_ptr, (segment.segCountX2 + 2)); CurCmap^.startCount := self.LoadCmapSegment(encode_ptr, CurCmap^.segment_count); encode_ptr := TTF_Helpers.Increment(encode_ptr, segment.segCountX2); CurCmap^.idDelta := self.LoadSignedCmapSegment(encode_ptr, CurCmap^.segment_count); encode_ptr := TTF_Helpers.Increment(encode_ptr, segment.segCountX2); CurCmap^.idRangeOffset := self.LoadCmapSegment(encode_ptr, CurCmap^.segment_count); index_array_size := Cardinal((8 + (4 * CurCmap^.segment_count)) * 2); index_array_size := ((inherited length - index_array_size) div 2); encode_ptr := TTF_Helpers.Increment(encode_ptr, segment.segCountX2); CurCmap^.GlyphIndexArray := self.LoadCmapSegment(encode_ptr, index_array_size); end; end end; inc(j); continue; ; end end; function CmapTableClass.LoadSignedCmapSegment(segment_ptr: Pointer; segment_count: Integer ): TSmallintArray; var i: Integer; p : PWord; begin SetLength(Result, segment_count); p := segment_ptr; i := 0; while ((i < segment_count)) do begin {$R-} Result[i] := TTF_Helpers.SwapInt16(p^); {$R+} inc(i); inc(p); end; end; { TCmapByteEncodingSubTable } function TCmapByteEncodingSubTable.GetGlyphIndex(ch: Byte): Word; begin Result := FGlyphIndexArray[ch]; end; end.