Files
ofdgo/ofdgo_font_cmap.go
T
2026-09-27 15:26:10 +08:00

123 lines
3.7 KiB
Go

// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ofdgo
import (
"bytes"
"encoding/binary"
"fmt"
)
// normalizeCmap 转换旧式双字节子表,保留各编码记录及原字形编号
// 入参: data 字符映射表
// 返回: []byte 等价字符映射表, error 子表错误
func normalizeCmap(data []byte) ([]byte, error) {
if len(data) < 4 {
return data, nil
}
count := int(binary.BigEndian.Uint16(data[2:]))
if count > (len(data)-4)/8 {
return nil, fmt.Errorf("invalid cmap encoding records")
}
var out []byte
converted := make(map[uint32]uint32)
for n := 0; n < count; n++ {
record := 8 + 8*n
offset := binary.BigEndian.Uint32(data[record:])
if uint64(offset)+2 > uint64(len(data)) {
return nil, fmt.Errorf("invalid cmap subtable offset")
}
if binary.BigEndian.Uint16(data[offset:]) != 2 {
continue
}
if out == nil {
out = bytes.Clone(data)
}
next, ok := converted[offset]
if !ok {
mapping, err := parseCmapFormat2(data[offset:])
if err != nil {
return nil, err
}
table := buildCmapTable(0, mapping)
subtable := table[binary.BigEndian.Uint32(table[8:]):]
language := binary.BigEndian.Uint16(data[offset+4:])
if binary.BigEndian.Uint16(subtable) == 12 {
binary.BigEndian.PutUint32(subtable[8:], uint32(language))
} else {
binary.BigEndian.PutUint16(subtable[4:], language)
}
next = uint32(len(out))
converted[offset] = next
out = append(out, subtable...)
}
binary.BigEndian.PutUint32(out[record:], next)
}
if out == nil {
return data, nil
}
return out, nil
}
// parseCmapFormat2 解析混合单字节与双字节字符的子头映射
// 入参: data format 2子表
// 返回: map[rune]uint16 编码值到字形编号, error 子头或字形数组越界
func parseCmapFormat2(data []byte) (map[rune]uint16, error) {
if len(data) < 526 {
return nil, fmt.Errorf("truncated cmap format 2")
}
length := int(binary.BigEndian.Uint16(data[2:]))
if length < 526 || length > len(data) {
return nil, fmt.Errorf("invalid cmap format 2 length")
}
data = data[:length]
maxKey := 0
for n := 0; n < 256; n++ {
key := int(binary.BigEndian.Uint16(data[6+2*n:]))
if key%8 != 0 || 518+key+8 > len(data) {
return nil, fmt.Errorf("invalid cmap format 2 subheader")
}
maxKey = max(maxKey, key)
}
mapping := make(map[rune]uint16)
for high := 0; high < 256; high++ {
key := 0
if high != 0 {
key = int(binary.BigEndian.Uint16(data[6+2*high:]))
if key == 0 {
continue
}
}
pos := 518 + key
first, count := int(binary.BigEndian.Uint16(data[pos:])), int(binary.BigEndian.Uint16(data[pos+2:]))
delta := binary.BigEndian.Uint16(data[pos+4:])
start := pos + 6 + int(binary.BigEndian.Uint16(data[pos+6:]))
if first+count > 256 || count > 0 && (start < 526+maxKey || start+count*2 > len(data)) {
return nil, fmt.Errorf("invalid cmap format 2 glyph array")
}
for n := 0; n < count; n++ {
low := first + n
if high == 0 && binary.BigEndian.Uint16(data[6+2*low:]) != 0 {
continue
}
glyph := binary.BigEndian.Uint16(data[start+2*n:])
if glyph != 0 {
mapping[rune(high*256+low)] = glyph + delta
}
}
}
return mapping, nil
}