上周在一台全新的CentOS 7.9服务器上部署GeoServer环境是JDK8 Tomcat9 GeoServer 2.25。发布完第一批土地利用数据后我打开WMS预览地图里的中文标注全部变成了一排排方框类似Win98时代没装中文字库打开网页的效果。当时同事第一反应是编码有问题改了一下午字符集结果毫无变化。这个坑我在过去几年里踩过七八回每次都能绕进同一个误判里。这次干脆把GeoServer在CentOS下中文变方框的完整定位思路和终极解法整理出来一次说透。先说结论在CentOS上部署GeoServer后地图文字显示为方框绝大多数不是编码问题而是Linux服务器缺少中文字体。GeoServer负责渲染地图标注时会把文本交给Java的图形渲染层去画Java在Linux下需要从系统字体库里找字形找不到中文字形就只能输出一个豆腐块方框。整个过程跟Shapefile属性乱码、数据库编码、Tomcat编码完全是两码事。后面的内容我会从症状区分、根因定位、安装字体、SLD联动到防复发按照实际排查顺序一步步来。1. 先分清你到底踩了哪种乱码方框、问号和锟斤拷是三个世界1.1 显示方框与属性乱码的本质差异中文显示异常有几种完全不同的表现网上搜出来的解决方案混在一起经常让人做无用功。最典型的有三种方框/豆腐块ToFu每个汉字显示成一个中空的方框部分版本还会带个十六进制数字。这是字形缺失意思是渲染引擎已经拿到正确的字符编码但在字体库里找不到对应的汉字轮廓。问号?????通常是数据读取时字符集转码失败。比如Shapefile的DBF文件是GBK编码GeoServer默认按UTF-8去解析读出来就变成问号。锟斤拷/烫烫烫这是UTF-8字节流被GBK解码再转回UTF-8后遗留的乱码多见于历史数据迁移、老系统导出的文本文件。我遇到的地图标注方框就属于第一种。编码层面一切正常数据库里的中文没有问题GeoServer内部字符串也没有问题只是最后一步把汉字画出来的时候字体库找不到中文字形。理解这一点非常重要因为后续所有操作都围绕让JVM能找到中文字体展开而不是去折腾URIEncoding或者characterEncoding。1.2 一张表快速自诊四种典型症状对应的问题范围我在排查现场一般会让同事先看症状属于哪一类再决定往哪个方向查症状表现常见出现位置根因方向地图标注显示□□□方框WMS预览、图例、打印PDF系统缺少中文字体、SLD字体族名不匹配属性表字段返回????问号WFS查询、要素属性、图层预览表Shapefile的DBF字符集、数据库连接串缺少UTF-8图层名/工作区名乱码管理后台数据区Tomcat的URIEncoding、GeoServer内部编码配置后台页面文字乱码GeoServer管理页面本身Tomcat或浏览器字符集强制错误这张表不绝对但能帮你快速缩小排查范围。我见过很多人在第一行的问题里折腾第三行的解决方案比如给Tomcat加编码过滤器去解决方框问题搞了半天完全没用。记住方框字形缺失问号编码错误这是两条路。1.3 我在现场最常见的误判有一次客户报障说地图上所有中文变方框我先远程看了下GeoServer日志没报任何错再看了数据库连接串已经是UTF-8又检查了Tomcat的server.xmlURIEncoding也是对的。所有编码环节都没有问题但方框就在那里。后来我才意识到问题根本不在数据链路而是这台服务器装的是CentOS最小化安装系统里压根没有任何中文字体。这类误判在论坛里太常见了。很多人一看到中文变方块条件反射就搜索乱码解决方案然后往编码方向投入大量时间。说实话第一次遇到时我自己也绕了弯路。所以这篇博文的第一个价值点就是别用查编码的思路查方框。后面所有方案都建立在字体缺失这个根因上。2. 根因定位CentOS最小化服务器的隐形缺字体排查链路2.1 为什么CentOS装了GeoServer后一定会显示方框CentOS 7以后的服务器安装很多人习惯选择Minimal模式连桌面环境和GUI都不装这样系统体积最小。但字体库这块也被裁掉了系统只保留最基础的英文字体中文字体一个都没有。更麻烦的是GeoServer自身只携带Java平台的逻辑字体Serif、SansSerif、Monospaced不带任何系统的字体文件。所以一旦SLD里写着要渲染中文Java2D渲染器会在系统字体目录和fontconfig配置里去匹配字形结果是根本没有可用的中文字体。有人会问那我本机Windows上跑GeoServer怎么好好的因为Windows自带宋体、黑体、微软雅黑等大量中文字体Java装上去就能直接用。Linux服务器不一样尤其是云服务器/内网机房的CentOS默认就是不给你带中文字体的。2.2 三步确认系统是否缺中文字体在动手装字体之前先花一分钟确认根因。按顺序执行这三步第一步系统字体列表里有没有中文字体fc-list :langzh如果输出为空或者提示No fonts found就说明系统确实没有注册任何中文字体。这一步基本就能下判断了。如果输出里有中文字体信息比如WenQuanYi Micro Hei那系统层面不缺字体问题在别处。第二步查看字体目录里的实际内容ls -l /usr/share/fonts/CentOS默认的字体目录是/usr/share/fonts/里面通常有dejavu、urw-base35这类英文字体目录。如果看不到任何中文相关的目录说明没有中文字体文件。有些管理员会往/usr/share/fonts/里塞.ttf文件但没有执行fc-cache导致字体文件在但未被系统注册这种情况也会显示方框。第三步去GeoServer后台看Fonts列表登录GeoServer管理界面找到服务器菜单下的字体页面。这里会列出当前JVM可识别的所有字体族名通常只有Dialog、DialogInput、Monospaced、SansSerif、Serif五种逻辑字体。如果你看不到任何中文字体族名就彻底实锤了GeoSerever连一个中文字体都没读到。这三步走完基本能确定是不是字体缺失。我建议至少执行第一步和第三步因为系统有字体不代表Java一定能读到后台Fonts列表才是最权威的结论。2.3 排查过程中容易踩的假线索排查阶段有几个假线索特别浪费时间和精力我说一下GeoServer日志无报错。字体缺失的时候GeoServer通常不会在日志里打任何Error只会在图片输出里画方框所以日志没报错不能排除字体问题。把锅甩给Tomcat编码。Tomcat的URIEncoding、useBodyEncodingForURI只影响HTTP请求里的参数解析和最后渲染字形的方框没有直接关系。以为是数据源头坏了。如果你发布的是PostGIS数据可能会怀疑是不是数据库里存的中文有问题。用pgAdmin或者QGIS直接连库看中文显示正常那数据没问题问题在渲染端。这类假线索之所以迷惑人是因为方框这个症状太容易被归类为乱码而乱码又太容易让人联想到编码配置。我个人的排查习惯是遇到地图文字方框先看Fonts列表而不是先翻server.xml。3. 终极解决方案把中文字体装进JVM能读到的位置3.1 方案Ayum安装开源中文字体别再手动传字体文件了这是最省事、也最推荐的方案直接用包管理器安装开源的文泉驿字体。在CentOS 7上# 如果还没启用EPEL先装epel-release yum install -y epel-release # 安装文泉驿微米黑和正黑 yum install -y wqy-microhei-fonts wqy-zenhei-fonts如果仓库里没有这两个包或者你更习惯明体系字体可以装yum install -y cjkuni-uming-fonts cjkuni-ukai-fonts在CentOS 8/9 Stream或者Rocky Linux 8/9上把yum换成dnf即可dnf install -y wqy-microhei-fonts wqy-zenhei-fonts如果公司要求使用Noto字体Google出品的开源字体族显示效果更现代也可以装Noto CJK# CentOS 7可能需要先启用epel yum install -y google-noto-sans-cjk-fonts装完之后文泉驿微米黑对应的字体族名是WenQuanYi Micro Hei文泉驿正黑对应WenQuanYi Zen HeiNoto Sans CJK SC对应Noto Sans CJK SC这些名字后面在SLD里要直接用到提前记下来。3.2 方案B离线服务器的字体上传与注册内网环境或没有公网源的时候从外部拷贝字体文件是常见做法。这也是很多政企项目的真实场景毕竟GIS服务器多数在内网机房。操作流程# 1. 创建字体目录 mkdir -p /usr/share/fonts/chinese # 2. 用sftp/scp把字体文件传上去比如simhei.ttf、NotoSansSC-Regular.otf # 例如 scp simhei.ttf root服务器IP:/usr/share/fonts/chinese/ # 3. 给字体文件加上可读权限 chmod -R 644 /usr/share/fonts/chinese/ # 4. 更新字体缓存 fc-cache -fv这里有个容易被忽略的坑Java老版本对.ttcTrueType Collection字体的支持不稳定。如果你从Windows系统目录里拷simsun.ttc、msyh.ttc这类集合文件很可能放在fc-list里能看到字体但渲染中文时Java还是画方框。能传.ttf或.otf就优先传这两种格式比如单文件的simhei.ttf黑体就比simsun.ttc宋体靠谱。另外从Windows拷贝的商用字体会涉及授权问题内部使用还好对外提供服务建议选文泉驿或者Noto这类开源字体。3.3 刷新fontconfig缓存让系统先看见字体不管是yum装的还是手动上传的字体文件并不会自动被系统识别。Linux的字体查找机制依赖fontconfig的缓存索引所以必须刷新一次fc-cache -fv正常输出里能看到类似/usr/share/fonts/chinese: caching, new cache contents: 1 fonts的字样。然后再次验证fc-list :langzh如果能看到中文字体列表说明系统层面已经认到了。3.4 设置JVM无头渲染参数别让Java走弯路字体装好了系统也认到了但JVM不一定马上能用。这里有一个关键参数java.awt.headless。GeoServer在服务器端渲染地图本质上跑在无图形界面的环境里必须明确告诉JVM进入无头模式否则字体渲染逻辑走的是带头模式的路径容易出现莫名其妙的字体加载失败。推荐把JVM参数加到Tomcat的启动环境变量中。以Tomcat为例修改bin/setenv.sh没有就创建这个文件export CATALINA_OPTS-Djava.awt.headlesstrue -Dfile.encodingUTF-8 -Xms512m -Xmx2048m如果你用的是CentOS自带的tomcat服务可以修改/etc/tomcat/tomcat.conf里的JAVA_OPTS。-Dfile.encodingUTF-8也很重要虽然它不是方框问题的直接根因但能避免Java内部字节流转换时出现字符集歧义属于顺手的防护配置。还有一个更底层的兜底方案直接把字体文件复制到JVM自己的字体目录里绕开fontconfig的桥接。以JDK8为例# 找到JAVA_HOME echo $JAVA_HOME # 创建fallback目录 mkdir -p $JAVA_HOME/jre/lib/fonts/fallback # 把字体复制过去注意优先ttf/otf cp /usr/share/fonts/chinese/wqy-microhei.ttc $JAVA_HOME/jre/lib/fonts/fallback/这种方式比较粗暴但能解决一些极端情况比如Java版本较老、fontconfig桥接失效。做这一步有个前提你装的是Oracle JDK或者OpenJDK路径里确实有jre/lib/fonts目录。如果用的是打了精简补丁的JDK目录结构可能不同先检查一下再动手。3.5 重启后的第一件事回后台看Fonts列表所有字体配置完成后重启Tomcat# 如果你用systemd管理tomcat systemctl restart tomcat # 如果是手动安装的tomcat /opt/tomcat/bin/shutdown.sh /opt/tomcat/bin/startup.sh重启完重新打开GeoServer管理后台的字体页面你会看到字体列表里多出不少家族常见的包括WenQuanYi Micro Hei、WenQuanYi Zen Hei以及系统逻辑字体。到这一步系统的字体供给链路已经打通地图标注不再画方框了。但请注意到这里只算完成了60%。剩下40%的问题是SLD样式里是否引用了正确字体族名、打印模块是否识别字体、GeoWebCache缓存是否还在吐旧瓦片这些恰恰是最容易让人装完字体还显示方框的原因。4. SLD样式、WMS输出与打印模块字体联动三连击4.1 SLD字体族名写宋体没用要用系统注册的族名这是我认为整个流程里最容易再次翻车的地方。很多人在SLD里写CssParameter namefont-family宋体/CssParameter然后在Linux服务器上完全没有效果。原因很简单系统里根本没有注册名为宋体的字体族。即使你装了中文字体它注册的族名也可能是WenQuanYi Micro Hei、AR PL UMing CN这类拼音/英文名称。SLD里font-family必须匹配系统实际注册的字体族名否则GeoServer会回退到默认字体方框依然存在。所以在写SLD之前回到后台Fonts页面用页面上显示的实际字体族名去填。常见对应关系安装的字体包实际字体族名wqy-microhei-fontsWenQuanYi Micro Heiwqy-zenhei-fontsWenQuanYi Zen Heicjkuni-uming-fontsAR PL UMing CNgoogle-noto-sans-cjk-fontsNoto Sans CJK SC我一般建议团队统一用Noto Sans CJK SC或者WenQuanYi Micro Hei一个作为标准字体族名写进所有SLD模板方便后期统一调整。4.2 一份可直接复用的中文标注SLD模板下面是一份完整的SLD可用于点图层的名称标注font-family已经写成WenQuanYi Micro Hei?xml version1.0 encodingUTF-8? StyledLayerDescriptor version1.0.0 xmlnshttp://www.opengis.net/sld xmlns:ogchttp://www.opengis.net/ogc xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd UserLayer Namelabel_zh/Name UserStyle Namelabel_zh/Name FeatureTypeStyle Rule TextSymbolizer Label ogc:PropertyNamename/ogc:PropertyName /Label Font CssParameter namefont-familyWenQuanYi Micro Hei/CssParameter CssParameter namefont-size14/CssParameter CssParameter namefont-stylenormal/CssParameter CssParameter namefont-weightnormal/CssParameter /Font Fill CssParameter namefill#000000/CssParameter /Fill /TextSymbolizer /Rule /FeatureTypeStyle /UserStyle /UserLayer /StyledLayerDescriptor在这个模板里ogc:PropertyNamename/ogc:PropertyName读取数据源里的name字段作为标注内容。如果你的字段名不同替换成实际字段名即可。保存为label_zh.sld在GeoServer发布图层样式的页面里上传使用。这里我补充一个常见经验如果使用WMS请求时通过STYLES样式名引用SLD请确保URL里的样式名和图层参数都是UTF-8编码的。GeoServer的WMS服务在解析中文图层名/样式名时跟Tomcat的URIEncoding有一定关系。Tomcat 8以上默认URI编码是UTF-8问题不大。但如果你的Tomcat手动改过server.xml里的URIEncodingGBK那就要小心WMS请求里带中文会挂掉。4.3 WMS出图和GeoWebCache的缓存清理装好字体、改好SLD之后很多人的第一反应是刷新浏览器预览页面结果发现还是方框。别急大概率是GeoWebCache把旧的瓦片缓存下来了。GeoServer内置了瓦片缓存当你请求http://ip:8080/geoserver/gwc/service/wms这类带gwc路径的服务时第一次渲染的图片会被缓存起来。字体修复前生成的瓦片如果还在缓存中WMS就一直是旧图。解决方式临时验证请求时加个随机参数比如_1234567890强制绕过缓存。彻底清理进入GeoServer后台的瓦片缓存管理页面找到对应图层清空GWC缓存。如果用了独立的GeoWebCache删除gwc缓存目录下的相关图层文件夹。我个人的建议是字体修复和SLD改造完成之后先强制刷新一次WMS预览确认确实好了再去清理全部瓦片缓存。不要一上来就清缓存否则会把样式调整前和样式调整后的瓦片混在一起搞不清楚到底是哪个环节生效了。4.4 打印模块的字体目录也要单独照顾如果你启用了GeoServer的打印扩展Print模块用来输出PDF那么这里还有另一个独立的字体环节。打印模块基于MapFish Print引擎它在自己的进程里渲染文字不直接复用GeoServer主进程的字体列表。有时候地图预览已经正常但打印出来的PDF依然中文变方框问题就出在打印模块没有读到中文字体。打印模块的字体配置通常在它的配置文件print/config.yaml或类似文件中。解决思路有两个确保打印模块的JVM能读到系统字体。如果打印模块和GeoServer跑在同一个Tomcat里通常重启Tomcat后就能生效。在打印配置里显式指定字体目录和字体族名。比如你安装的是WenQuanYi Micro Hei在配置里把字体族名设置成WenQuanYi Micro Hei而不是默认的Helvetica。要验证打印模块是否已经可用中文字体最直接的办法是打印一份带中文的PDF看看。如果还是方框先检查打印模块的配置文件里是否额外指定了字体目录把/usr/share/fonts/chinese加进去再重启服务。5. 防复发检查清单与扩展从命令行到浏览器的完整验证5.1 一分钟自检命令新机器开箱检查说实话这条路走通一次实在不容易。我在内部团队里沉淀了一套新服务器开箱检查的脚本思路每次部署完GeoServer按这个顺序跑一遍基本能在发布数据之前就发现字体隐患。# 1. 看系统有没有中文字体 fc-list :langzh # 2. 看关键字体目录 ls -l /usr/share/fonts/ | grep -i -E wqy|noto|cjk|chinese # 3. 确认JAVA_HOME和Java版本 echo $JAVA_HOME java -version # 4. 确认Tomcat的JVM参数 cat /opt/tomcat/bin/setenv.sh 2/dev/null || cat /etc/tomcat/tomcat.conf | grep -i java_opts跑完这四条命令你基本能判断这台机器是否具备中文渲染能力。如果fc-list :langzh输出为空直接在部署阶段就装字体别等上线后再来回折腾。5.2 容易被误判成乱码的其他场景DBF编码、连接串、Tomcat编码虽然方框问题主要是字体缺失但有一类假方框值得提一嘴。当你发布一个Shapefile图层时如果属性表里的中文在图层预览中显示成问号而不是方框那是DBF字符集的问题跟字体无关。Shpfile的编码由.dbf文件自带的信息决定而GeoServer在Store配置里有一个DBF字符集选项。遇到中文乱码把它从UTF-8改成GBK或者GB2312试试这是发布Shapefile的老问题了。PostGIS数据源同理连接串里加上jdbc:postgresql://127.0.0.1:5432/gisdb?characterEncodingUTF-8这个参数指定了JDBC连接使用UTF-8进行字符集转换避免读取中文属性时变成问号。还有一类场景GeoServer后台界面本身出现中文乱码比如登录页、菜单。那通常不是字体问题而是浏览器强制指定了错误字符集或者Tomcat的URIEncoding配置成非UTF-8。你可以在Tomcat的server.xml里确认Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 URIEncodingUTF-8 useBodyEncodingForURItrue /这些属于数据链路编码的问题虽然不直接产生方框但经常和字体问题一起被混着搜所以放在防复发清单里一并提醒。5.3 团队级固化把字体安装写进初始化脚本最后说点实战层面的经验。这种显示方框的问题最怕的就是每次部署新服务器都要重新踩一遍。我建议把字体安装整理成一段原子脚本固化在服务器的初始化配置里。以Ansible为例可以写一个简单的playbook- name: Install Chinese fonts for GeoServer hosts: geoserver tasks: - name: Install font packages yum: name: - wqy-microhei-fonts - wqy-zenhei-fonts - google-noto-sans-cjk-fonts state: present when: ansible_os_family RedHat - name: Refresh font cache command: fc-cache -fv即使不用Ansible也可以直接维护一小段Shell脚本装完系统后先跑一遍再装GeoServer。这样后续新环境上线基本不会再出现中文方框的报障。我个人的实际体会是这类问题90%出在三个环节系统字体缺失、SLD字体族名写错、瓦片缓存未清。只要把这三关一一验证到位剩下的10%基本就是打印模块和DBF编码这些边角问题。希望这篇整理能让你少走几趟弯路一次把GeoServer的中文显示问题彻底解决。