Flutter中如何在图片文件上绘制文字并解决iOS端文字溢出裁剪问题
问题根因
- 你使用的
image库自带的drawString方法本身不支持自动换行,你当前是把整段文字当做单行计算宽度,当经纬度位数较多、iOS端字体渲染字符宽度和预设的Arial14字符宽度有差异时,就会出现横向超出边界被裁剪的问题 - 你当前的宽高计算没有预留安全边距,文字直接贴在图片右下角边缘,也容易出现边缘裁剪
- 字符宽度计算逻辑没有处理换行符,会把换行符的宽度也计入总宽度,导致最终绘制位置计算错误
解决方案
核心修改点
- 先预设右下角可绘制区域的安全边距,避免文字贴边
- 手动实现文字自动换行逻辑,按照可绘制区域的最大宽度拆分文本为多行
- 增加跨端渲染兼容系数,适配iOS端字体渲染的宽度差异
- 逐行绘制文字,计算每行的偏移量,确保所有行都在图片范围内
完整修改后代码
Future<File?> drawTextOnImage() async { final ImagePicker _picker = ImagePicker(); // 拍摄照片 final image = await _picker.pickImage( source: ImageSource.camera, maxHeight: 366, maxWidth: 550, imageQuality: 50); final position = await locationManager.getCurrentPosition( desiredAccuracy: LocationAccuracy.high); if (image != null) { var decodeImg = UIImage.decodeImage(File(image.path).readAsBytesSync()); final textToDraw = '纬度: ${position.latitude}\n经度:${position.longitude}'; const padding = 15.0; // 右下角预留安全边距,可根据需求调整 const font = UIImage.arial_14; // 计算可绘制区域的最大宽度 final maxDrawWidth = decodeImg!.width - padding * 2; // 兼容iOS字体渲染的宽度系数,可根据实际渲染效果微调 const widthScale = 1.1; // 自动换行拆分文本 List<String> lines = []; List<String> paragraphs = textToDraw.split('\n'); for (String para in paragraphs) { List<String> words = para.split(' '); String currentLine = ''; double currentWidth = 0; for (String word in words) { // 计算当前单词加空格的宽度 double wordWidth = 0; String wordWithSpace = word + ' '; for (var c in wordWithSpace.codeUnits) { if (font.characters.containsKey(c)) { wordWidth += font.characters[c]!.xadvance * widthScale; } } if (currentWidth + wordWidth <= maxDrawWidth) { currentLine += wordWithSpace; currentWidth += wordWidth; } else { lines.add(currentLine.trim()); currentLine = wordWithSpace; currentWidth = wordWidth; } } if (currentLine.isNotEmpty) { lines.add(currentLine.trim()); } } // 计算总文本高度 double lineHeight = (font.lineHeight).toDouble(); double totalTextHeight = lines.length * lineHeight; // 计算绘制起始点(右下角对齐) double startX = decodeImg.width - padding - maxDrawWidth; double startY = decodeImg.height - padding - totalTextHeight; // 逐行绘制文字 for (int i = 0; i < lines.length; i++) { double lineY = startY + i * lineHeight; UIImage.drawString(decodeImg, font, startX.toInt(), lineY.toInt(), lines[i]); } var encodeImage = UIImage.encodeJpg(decodeImg, quality: 100); var finalImage = File(image.path)..writeAsBytesSync(encodeImage); return finalImage; } helper.ToastFlutter('获取图片失败,请重试'); return null; }
可选优化
如果对字体渲染一致性要求更高,可以自己加载字体文件替代库自带的Arial字体,避免不同系统自带字体差异导致的宽度计算误差。
内容的提问来源于stack exchange,提问作者Surjava Ghosh
相关产品推荐
相关产品推荐

