记一次前端文本对齐的问题
 
                            
                           
                          
                         
                        
                       
                      
                     
                    
    
前段时间处理了一个在网页中文本对齐的问题,发现了一些之前关于字体未曾了解的知识点,颇有意思,总结一下。
<!--more-->
1. 背景
                      
                       
                        
                         
                          
                           
                            
                             
                              
                               业务中需要在网页中展示
                               
                                pandas
                               
                               读取excel后的输出内容
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
import pandas as pd
import sys
pd.set_option('display.unicode.ambiguous_as_wide', True)
pd.set_option('display.unicode.east_asian_width', True)
data = pd.read_excel("./销售订单.xlsx", sheet_name="订单数据")
# sys.stdout = open("1.log", "w") # 测试输出重定向
print(data)控制台打印的效果十分完美
 
                                
                               
                              
                             
                            
                           
                          
                         
                        
                       
                      
                       
                        
                         
                          
                           
                            
                             
                              
                               在浏览器中使用
                               
                                pre
                               
                               标签展示输出内容时,却发现文本完全没有像控制台那样对齐
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
    
 
                                
                               
                              
                             
                            
                           
                          
                         
                        
                       下面是原始输出内容
<pre>             订单号  商品ID            商品名      品牌  类别     规格  单价  数量  总价            下单时间
0    98232019040002  700009        芹菜味薯片  开口哭牌  零食    200克    20     2    40 2019-04-01 08:00:15
1    98232019040003  700006  没有一点味口香糖  开口哭牌  零食    100克    15     2    30 2019-04-01 08:45:10
2    98232019040004  700013        火龙果可乐  君再来牌  饮料  550毫升     5     5    25 2019-04-01 10:03:38
3    98232019040005  700003      芹菜味口香糖  开口哭牌  零食    100克    15     1    15 2019-04-01 10:58:03
4    98232019040006  700009        芹菜味薯片  开口哭牌  零食    200克    20     1    20 2019-04-01 11:35:19
..              ...     ...               ...       ...   ...      ...   ...   ...   ...                 ...
300  98232019040302  700011        蟹黄味薯片  开口哭牌  零食    200克    20     5   100 2019-04-30 13:29:20
301  98232019040303  700016        夏夜雨可乐  君再来牌  饮料  550毫升     5     1     5 2019-04-30 14:05:13
302  98232019040304  700015        青草味可乐  君再来牌  饮料  550毫升     5     1     5 2019-04-30 14:45:06
303  98232019040305  700015        青草味可乐  君再来牌  饮料  550毫升     5     1     5 2019-04-30 17:46:43
304  98232019040306  700005      蟹黄味口香糖  开口哭牌  零食    100克    15     2    30 2019-04-30 22:07:13
[305 rows x 10 columns]</pre>
                      
                       
                        
                         
                          
                           
                            
                             
                              
                               最开始以为是在复制文本时导致空格被合并了,因此使用
                               
                                sys.stdout
                               
                               将输出重定向到文本中,然后使用VSCode打开,发现居然也是错乱的
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
 
                                
                               
                              
                             
                            
                           
                          
                         
                        
                       2. 使用严格半角的字体
经过非常严格和认真的对比,我发现这些文本是通过填充不同的空格进行对齐的,换言之,如果需要对齐,字体需要满足下面的条件
- 英文字体等宽,且与一个空格的宽度相等
- 中文字体等宽
- 一个中文字符等于两个空格的宽度
这里需要配置符合下面要求的严格半角字体,参考:
                      
                       
                        
                         
                          
                           
                            
                             
                              
                               在第二个回答中找到了Mac系统自带的一种字体
                               
                                PCMyungjo
                               
                               ,试了一下
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
pre {
    font-family: PCMyungjo;
}尽管部分标签符号和汉字看起来有点奇怪,居然能满足要求!!而这也仅仅需要一行简单的CSS代码。
 
                                
                               
                              
                             
                            
                           
                          
                         
                        
                       
    
当然,随之而来的就是兼容性问题,并不能保证所以机器上都安装了该字体,且该字体并不能通过UI那关,因此尝试去寻找了一些其他符合条件的字体。
                      
                       
                        
                         
                          
                           
                            
                             
                              
                               后来发现如
                               
                                SimHei
                               
                               等黑体也可以满足条件,且汉字展示要美观得多
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
@font-face {
  font-family: "SimHei";
  src: url("SimHei.ttf") format("truetype");
  font-weight: normal; 
  font-style: normal;
  font-display: swap;
pre {
    font-family: SimHei;
} 
                                
                               
                              
                             
                            
                           
                          
                         
                        
                       中文自定义字体的缺点在于体积往往会非常大
 
                                
                               
                              
                             
                            
                           
                          
                         
                        
                       因此还需要寻找其他不依赖于特定字体的其他方案。
这里补充一下关于字体的一些知识
3. 等宽字体
参考: 等宽字体 - 维基百科
等宽字体(英语:Monospaced Font)是指字符宽度相同的电脑字体。与此相对,字符宽度不尽相同的电脑字体称为比例字体。
由于早期打字机和显示器等技术局限,字符一般也是等宽的。在传统西文印刷中,比例字体可以提高单词的可读性。目前由于技术突破,比例字体的使用也比较普及
大部分程序员选择的代码字体一般都是等宽的,等宽字体在处理缩进对齐、统一字符间距等方面更占优势;此外,东亚字体中的方块字基本上都作为等宽字体处理。
4. 全角半角字体
参考:
主要原因是 符号冲突
比如英文逗号","与中文逗号",",用眼睛就可以看出长度与大小是不一样的。当在键盘上输入逗号时,中文输入法不确定你想要的是哪种逗号(中/英),所以就提供了全角半角模式,英文半角输出英文逗号,其它模式就是中文逗号,这样,我们用一种输入法就能打出两种符号,而不用切换成其它输入法
5. 控制每个中文字符的宽度
由于VSCode编辑框与终端默认配置的是相同的字体,因此编辑框和终端展示结果不一致应该不是字体的问题。那为啥终端会展示完全对齐的效果呢?
后来发现了一个类似的issue: Print data.frame with Chinese strings column aligned
其中提到了一个解决办法是手动控制设置每个中文字符的宽度~咋一看貌似挺不靠谱,转念一想:咦?貌似值得一试
function getTextSize(parent, ch = 'a') {
  const span = document.createElement('span')
  const result = {}
  result.width = span.offsetWidth
  result.height = span.offsetHeight
  span.style.visibility = 'hidden'
  // span.style.fontSize = fontSize
  // span.style.fontFamily = fontFamily
  span.style.display = 'inline-block'
  parent.appendChild(span) // 使用继承自父元素的字体样式
  if (typeof span.textContent !== 'undefined') {
    span.textContent = ch
  } else {
    span.innerText = ch
  result.width = parseFloat(window.getComputedStyle(span).width) - result.width
  result.height = parseFloat(window.getComputedStyle(span).height) - result.height
  parent.removeChild(span)
  return result
}然后控制每个字符的宽度,为了减少内联style导致HTML内容过于复杂,可以使用CSS变量
let preDom = document.querySelector('#pre')
let preTextSize = getTextSize(preDom)
preDom.setAttribute("style", `--char-width:${preTextSize.width}px`);
                      
                       
                        
                         
                          
                           
                            
                             
                              
                               然后将所有的中文字符添加一个特定的样式类
                               
                                char-cn
                               
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
.char-cn {
    font-family: monospace;
    display: inline-block;
    text-align: center;
    width: calc(var(--char-width) * 2);
}
                      
                       
                        
                         
                          
                           
                            
                             
                              
                               最后将pre标签内容中的所有中文字符添加
                               
                                char-cn
                               
                               类,同时替换内容
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
function replaceChineseChar(str) {
    const re = /[\u4e00-\u9fa5]/gi


