Python使用freetype渲染显示阿拉伯语
目录
一、使用场景
二、语言背景
三、环境搭建
四、程序结构
五、代码
一、使用场景
公司一直以点阵屏显示为业务。最近希望替换原有的点阵字库,转用FreeType渲染矢量字形,且需要支持阿拉伯语。验证可行性阶段因为python的各种库用起来相当舒服,所以就先用Python进行验证,通过后再转C++实现。
二、语言背景
阿拉伯语与常规语言不同,它属于复杂文本语言。它有以下3个特点:
1.阅读顺序从右往左
2.字符在词前中后有不同写法
3.带有修饰符号
这里非常感谢建国雄心大哥的文章。推荐大家对于阿拉伯文如果不理解可以去他的博客看看。
http://blog.sina.com.cn/s/articlelist_1569506881_0_1.html
因为阿拉伯文有以上特点,所以不能单纯的一个字符一个字符的读取并渲染,在对字符串渲染之前需要经过一次特殊处理,转成正确的Unicode码串后再使用FreeTyoe对转换后的Unicode码串进行渲染。
三、环境搭建
这里需要用到3个Python库
1.numpy
引用原因:方便对点阵数组的操作, 而且Freetype好像也用到了
安装命令:pip install numpy
2.freetype-py
引用原因:矢量字体渲染模块
安装命令:pip install freetype-py
3.matplotlib
引用原因:显示渲染结果,如果需要在别的地方显示,可以不需要
安装命令:pip install matplotlib
四、程序结构
首先,对多语言的渲染大致分为两个模块,解析模块和渲染模块。解析模块用于处理原始Unicode码;渲染模块根据Unicode码取字模并合成点阵数组。
流程大致如下:原始字符串 -> 解析模块 -> 处理后Unicode码串 -> 渲染模块 -> 点阵数组 -> 显示。
这里的解析模块本人是自己写,但是推荐使用HarfBuzz。
这里放个用matplotlib显示的效果
五、 代码
解析模块
class ArabicText(object):# first, last, middle, alone __arabic_Positions=[[ 0xfe80, 0xfe80, 0xfe80, 0xfe80], #0x621[ 0xfe82, 0xfe81, 0xfe82, 0xfe81], [ 0xfe84, 0xfe83, 0xfe84, 0xfe83],[ 0xfe86, 0xfe85, 0xfe86, 0xfe85],[ 0xfe88, 0xfe87, 0xfe88, 0xfe87],[ 0xfe8a, 0xfe8b, 0xfe8c, 0xfe89], [ 0xfe8e, 0xfe8d, 0xfe8e, 0xfe8d], [ 0xfe90, 0xfe91, 0xfe92, 0xfe8f], [ 0xfe94, 0xfe93, 0xfe94, 0xfe93], [ 0xfe96, 0xfe97, 0xfe98, 0xfe95], [ 0xfe9a, 0xfe9b, 0xfe9c, 0xfe99], [ 0xfe9e, 0xfe9f, 0xfea0, 0xfe9d], [ 0xfea2, 0xfea3, 0xfea4, 0xfea1], [ 0xfea6, 0xfea7, 0xfea8, 0xfea5], [ 0xfeaa, 0xfea9, 0xfeaa, 0xfea9], [ 0xfeac, 0xfeab, 0xfeac, 0xfeab], [ 0xfeae, 0xfead, 0xfeae, 0xfead], [ 0xfeb0, 0xfeaf, 0xfeb0, 0xfeaf], [ 0xfeb2, 0xfeb3, 0xfeb4, 0xfeb1], [ 0xfeb6, 0xfeb7, 0xfeb8, 0xfeb5], [ 0xfeba, 0xfebb, 0xfebc, 0xfeb9], [ 0xfebe, 0xfebf, 0xfec0, 0xfebd], [ 0xfec2, 0xfec3, 0xfec4, 0xfec1], [ 0xfec6, 0xfec7, 0xfec8, 0xfec5], [ 0xfeca, 0xfecb, 0xfecc, 0xfec9], [ 0xfece, 0xfecf, 0xfed0, 0xfecd], [ 0x63b, 0x63b, 0x63b, 0x63b], [ 0x63c, 0x63c, 0x63c, 0x63c], [ 0x63d, 0x63d, 0x63d, 0x63d], [ 0x63e, 0x63e, 0x63e, 0x63e], [ 0x63f, 0x63f, 0x63f, 0x63f], [ 0x640, 0x640, 0x640, 0x640], [ 0xfed2, 0xfed3, 0xfed4, 0xfed1], [ 0xfed6, 0xfed7, 0xfed8, 0xfed5], [ 0xfeda, 0xfedb, 0xfedc, 0xfed9], [ 0xfede, 0xfedf, 0xfee0, 0xfedd], [ 0xfee2, 0xfee3, 0xfee4, 0xfee1], [ 0xfee6, 0xfee7, 0xfee8, 0xfee5], [ 0xfeea, 0xfeeb, 0xfeec, 0xfee9], [ 0xfeee, 0xfeed, 0xfeee, 0xfeed], [ 0xfef0, 0xfef3, 0xfef4, 0xfeef], [0xfef2, 0xfef3, 0xfef4, 0xfef1]]__preSet = [0x62c, 0x62d, 0x62e, 0x647, 0x639, 0x63a, 0x641, 0x642,0x62b, 0x635, 0x636, 0x637, 0x643, 0x645, 0x646, 0x62a, 0x644, 0x628, 0x64a, 0x633, 0x634, 0x638, 0x626, 0x640] __nextSet = [0x62c, 0x62d, 0x62e, 0x647, 0x639, 0x63a, 0x641, 0x642,0x62b, 0x635, 0x636, 0x637, 0x643, 0x645, 0x646, 0x62a, 0x644, 0x628, 0x64a, 0x633, 0x634, 0x638, 0x626, 0x627, 0x623, 0x625, 0x622, 0x62f, 0x630, 0x631, 0x632, 0x648, 0x624, 0x629, 0x649, 0x640]__replaceSet = [[0xFEF5,0xFEF6],[0xFEF7,0xFEF8],[0xFEF9,0xFEFA],[0xFEFB,0xFEFC]] # 将传入的字符串转换为显示时的数组,显示时用FreeType直接取数组中的每一个值进行排版显示即可 # 返回前已经将阿拉伯文倒置(阿拉伯文从右往左书写) @staticmethod def Translate(text):retArr = [] textLen = len(text)lastIdx = -3 # 上一个非阿拉伯字符所在下标 begIdxs = [] # 非阿拉伯字符串开始下标集合 endIdxs = [] # 非阿拉伯字符串结束下标集合for i in range(0,textLen): charCode = ord(text[i]) # 非阿拉伯语字符直接添加 if charCode not in range(0x621,0x6ff): retArr.append(charCode) arrLen = len(retArr) # 不连续 if arrLen - 1 != lastIdx + 1: begIdxs.append(arrLen - 1) # 最后一个字符是非阿拉伯字符 if i == textLen - 1: endIdxs.append(arrLen - 1) lastIdx = arrLen - 1 continue else: arrLen = len(retArr) # 当前阿拉伯字符的前一个字符是非阿拉伯字符 if lastIdx == arrLen-2: endIdxs.append(lastIdx)#----rule 1---- # 前一个字符的Unicode码 preCh = (0 if (i==0) else ord(text[i-1])) # preCh = i==0 ? 0 : (int)text[i-1] # 当前字符的Unicode码 ch = charCode # ch = (int)text[i] # 后一个字符的Unicode码 nextCh = (0 if(i==(textLen-1)) else ord(text[i+1])) # nextCh = i == (textLen-1) ? 0 : (int)text[i+1]val = ArabicText.__GetTransform(preCh,ch,nextCh) retArr.append(val) #----rule 2---- replace = ArabicText.__GetContinuousWriting(preCh,ch,nextCh) if replace > 0: retArr.append(replace) i = i + 2# 阿拉伯文从右往左显示,所以要把结果反过来 retArr.reverse() ArabicText.__NonArabicReverse(retArr,begIdxs,endIdxs) return retArr # 处理非阿拉伯字符,非阿拉伯字符不用反转,这里把他们再反回来 @classmethod def __NonArabicReverse(cls,charArr=[],begIdxs=[],endIdxs=[]): lastIdx = len(charArr) - 1 # 最后一个下标 loopCnt = len(begIdxs) for i in range(0,loopCnt): beg = (lastIdx - endIdxs[i]) end = (lastIdx - begIdxs[i]) switchTimes = int((end + 1 - beg)/2) for j in range(0,switchTimes): temp = charArr[beg+j] charArr[beg+j] = charArr[end-j] charArr[end-j] = temp# 处理连写字符 某些情况下需要将后续两个字符替换成其他字符 @classmethod def __GetContinuousWriting(cls,preCh=0,ch=0,nextCh=0): retVal = 0 nextChArr = [0x622,0x623,0x625,0x627] positionIdx = -1 charIdx = 0 if (ch == 0x644) and (nextCh in nextChArr): charIdx = nextChArr.index(nextCh) if preCh in cls.__preSet: positionIdx = 1 else: positionIdx = 0 retVal = cls.__replaceSet[charIdx][positionIdx] return retVal# 处理字符因前连写后连写的变形 @classmethod def __GetTransform(cls,preCh=0,ch=0,nextCh=0): preConnect = False nextConnect = False positionIdx = -1 charIdx = 0# 是前连字符 if preCh in cls.__preSet: preConnect = True positionIdx = 0# 是后连字符 if nextCh in cls.__nextSet: nextConnect = True positionIdx = 1# 既是前连又是后连,等于在中间 if preConnect and nextConnect: positionIdx = 2 # 不是前连又不是后连,等于要单独显示 elif (preConnect == False) and (nextConnect == False): positionIdx = 3charIdx = ch - 0x621 retVal = cls.__arabic_Positions[charIdx][positionIdx] return retVal
入口和渲染模块
# -*- coding: utf-8 -*-
import freetype
import numpy
import matplotlib.pyplot as plt
import ArabicTextHelper as Arabic
def main():text = u'شبح ، شبح الشيوعية ، يتجول في جميع أنحاء القارة الأوروبية' textArr = [] # 处理原始字符串,生成转换后的数组 textArr = Arabic.ArabicText.Translate(text=text)# 显示转换后的数组 FreeTypeDisplay(textArr,0x33,0xe4,0xff) def FreeTypeDisplay(textArr=[],R=255,G=255,B=255): RGB = [('R',numpy.uint8), ('G',numpy.uint8), ('B',numpy.uint8)] face = freetype.Face('Fonts/ARIALUNI.TTF') face.set_char_size( 48*64 ) slot = face.glyph# First pass to compute bboxwidth, height, = 0, 0 previous = 0 # 计算总宽高 for c in textArr: face.load_char(c) bitmap = slot.bitmap height = max(height, (face.size._FT_Size_Metrics.height >> 6)) kerning = face.get_kerning(previous, c) width += (slot.advance.x >> 6) + (kerning.x >> 6) previous = cimgBuf = numpy.zeros((height,width), dtype=numpy.ubyte) colorBuf = numpy.zeros((height,width),dtype=RGB)# Second pass for actual renderingxBeg, yBeg = 0, 0 previous = 0 # 把每个字添加到imgBuf里 for c in textArr: face.load_char(c) # 可以理解为校正值 descender = (-face.size._FT_Size_Metrics.descender) >> 6 bitmap = slot.bitmap #基线到字模顶部的距离 top = slot.bitmap_top w = bitmap.width h = bitmap.rows yBeg = height - top - descender # 间隔 kerning = face.get_kerning(previous, c) xBeg += (kerning.x >> 6) newChar = numpy.array(bitmap.buffer, dtype='ubyte').reshape(h,w) yEnd = yBeg+h xEnd = xBeg+w # 添加到imgBuf中 imgBuf[yBeg:yEnd,xBeg:xEnd] += newChar xBeg += (slot.advance.x >> 6) previous = cFillColor(imgBuf,colorBuf,R,G,B)# 显示imgBuf的内容 plt.figure(figsize=(10, 10*imgBuf.shape[0]/float(imgBuf.shape[1]))) showing = colorBuf.view(dtype=numpy.uint8).reshape(colorBuf.shape[0],colorBuf.shape[1],3) plt.imshow(showing, interpolation='nearest', origin='upper') plt.xticks([]), plt.yticks([]) plt.show()def FillColor(srcBuf,colorBuf,R,G,B): rows = srcBuf.shape[0] columns = srcBuf.shape[1] for y in range(0,rows): for x in range(0,columns): if srcBuf[y][x] > 0: colorBuf[y][x] = (R,G,B)if __name__ == '__main__': main()
参考文章
https://www.cnblogs.com/8335IT/p/8053850.html
https://blog.csdn.net/wuxinyanzi/article/details/12912533
Python使用freetype渲染显示阿拉伯语相关推荐
- 排列显示阿拉伯语、数字及英文时的处理方法
这段时间参与开发沙特阿拉伯的网站模板,模板使用的语言是阿拉伯语,其显示顺序为从右到左,而中文.数字及英文的显示顺序为从左到右 ,所以当遇到一行文字内既有阿拉伯语又有数字和英文时会出现语序混乱的问题,如 ...
- 如何显示在网页中显示阿拉伯语言
我记得以前在微软面试程序员的时候,面试官问怎么样去显示一个阿拉伯的网页.我们知道阿拉伯的网页的显示方法是从右到左,向右对齐的.由于当时没有接触到这种页面,真的不知道该怎么去实现. 今天,终于知道了(太 ...
- 多语言适配之阿拉伯语
多语言配置: 1.获取当前系统语言的方法: Locale locale = getApplicationContext().getResources().getConfiguration().loca ...
- python PyQt5中文教程☞【第二节】PyQt5基本功能(创建窗口、应用程序图标、显示提示语、通过按钮关闭窗口、消息框(关闭窗口确认框)、窗口显示在屏幕中间【居中显示】)
引用文章:http://code.py40.com/pyqt5/ 文章目录 简单的例子:创建一个小窗口 应用程序的图标 显示提示语 通过按钮关闭窗口 消息框(关闭窗口确认框) 窗口显示在屏幕的中间[居 ...
- 安卓 阿拉伯语显示时文字的适配
当手机设备的蓝牙名改为阿拉伯语或者维吾尔语时,该语言是RTL模式. 当DA侧连接到该设备时,会显示该设备的信息:[%1$s] is set as the priority device 此时默认位置会 ...
- Arabic(阿拉伯语)显示
最近一个vc的项目要求多语言,其中包括阿拉伯语,负责翻译的同桌把excel的语言文件发给我,用脚本把他转成xml后集成在vc工程里,然后发了个版本出去.测试的人马上就报了bug,他说阿拉伯语应该是从右 ...
- android 切换到阿拉伯语电话号码+号显示在右侧及顺序错乱的处理
1.来电通知栏电话号码+号显示在右侧的修改 位置:packages/apps/InCallUI/src/com/android/incallui/StatusBarNotifier.java 刚开始修 ...
- Python VTK Read Write 常用读写以及 渲染 显示
Python VTK 常用读写以及 渲染 显示 Part1 Common Reader Reader = vtk.vtkNIFTIImageReader()Reader.SetFileName('./ ...
- Unity阿拉伯语的适配(终极版)
最近在做阿语的适配,发现网上并没有一套完整的方案,这次给大家带来一套完整的解决方案,并配上一套可扩展的代码,希望我们每个人以后再遇到阿拉伯语的适配的时候,可以使用这套终极解决方案来搞定.同时随着uni ...
- 阿拉伯语排版设计_针对说阿拉伯语的用户的测试和设计
阿拉伯语排版设计 Let me start off with some data to put things into perspective "Why?" 让我从一些数据入手,以 ...
最新文章
- L1-009. N个数求和
- 过桥问题c语言程序,盏灯过桥游戏
- kettle内存溢出
- NLP之BoWNLTK:自然语言处理中常用的技术——词袋法Bow、NLTK库
- iOS native集成Weex js文件 不显示提示框问题
- openstack 使用集锦
- Linux下的/bin、/sbin、/usr/bin、/usr/sbin目录
- 现实版“奇异博士”?原来是这款神秘的“数学黑盒”
- Cere Network将在DAOMaker平台启动首次种子私募轮社区融资
- 开始使用 TypeScript
- 经典论文学习笔记——13篇对比学习(Contrastive Learning)
- I2C(smbus、pmbus)、SPI
- 性能测试之LoadRunner11脚本录制方法
- 代码review总结
- 初学者学吉他推荐,别因选错吉他而前功尽弃
- 用51单片机中断控制LED灯亮灭
- Java核心技术卷一 -第五章:装箱和拆箱
- 宿舍管理系统的设计与实现/学生宿舍管理系统
- 品牌对比 | 特步 VS 李宁
- linux宝塔杰奇安装,杰奇2.4安装教程+远程采集
热门文章
- 得力人脸识别考勤机密码设置_得力人脸识别考勤机使用与上传数据方法哪位清楚?...
- cad连筋字体怎么安装_CAD图纸乱码怎么办?送你2800款字体,解决烦人的乱码问题...
- (一)密码学之数论基础
- idea 安装jrebel6.4.3及破解
- 【地图API】地址录入时如何获得准确的经纬度?淘宝收货地址详解
- windows编译opencv+opencv_contrib 以及解决cmake下载boostdesc_bgm等文件失败问题
- java中的sqlist,JAVA连接SQList数据库 | 学步园
- LabelImg安装教程(已亲测)
- 国密算法c语言实现,求 国密sm2 算法 第四部分 公钥加密算法 c语言实现代码,该怎么解决...
- java 港澳台手机号码正则表达式