Golang处理Word文档模板实现标签填充|表格插入|图标绘制和插入|删除段落|删除标签
本教程主要实现【Golang处理Word文档模板实现标签填充|表格插入|图标绘制和插入|删除段落|删除标签】。
本文源码:https://gitee.com/songfayuan/go-zero-demo
教程源码分支:master 分支(_examples/word-template/fill-word-template.go)
Golang处理Word文档模板教程
本教程将指导您使用Golang处理Word文档模板,包括自定义标签填充、动态插入表格、绘制图表和插入图表。我们将使用unioffice
库和gg
库来完成这些任务。
前提条件
在开始之前,请确保您已安装以下库:
- unioffice
- gg
您可以使用以下命令安装这些库:
go get -u github.com/Esword618/unioffice
go get -u github.com/fogleman/gg
代码结构
我们的代码分为以下几部分:
- 打开Word文档
- 填充模板中的变量
- 在指定标签处插入表格
- 创建折线图并保存为图片
- 在指定标签处插入图表
- 保存更新后的文档
示例代码
以下是完整的示例代码:
package mainimport ("fmt""log""math""strings""github.com/Esword618/unioffice/color""github.com/Esword618/unioffice/common""github.com/Esword618/unioffice/document""github.com/Esword618/unioffice/measurement""github.com/Esword618/unioffice/schema/soo/wml""github.com/fogleman/gg"
)//教程:https://blog.csdn.net/u011019141/article/details/140788882func main() {// 定义文档路径和图表文件路径docPath := "_examples/word-template/template.docx"lineChartFile := "/Users/songfayuan/Downloads/line_chart.PNG"barChartFile := "/Users/songfayuan/Downloads/bar_chart.png"pieChartFile := "/Users/songfayuan/Downloads/pie_chart.png"updatedDocPath := "/Users/songfayuan/Downloads/updated_demo.docx"// 打开文档doc, err := openDocument(docPath)if err != nil {log.Fatalf("无法打开文档: %v", err)}// 填充模板中的变量fillTemplate(doc, map[string]string{"{{TASK_NAME}}": "任务名称示例","{{DETAILS}}": "详细信息示例",})// 在指定标签处插入表格if err := insertTableAt(doc, "{{biaoge}}"); err != nil {log.Fatalf("插入表格时出错: %v", err)}// 创建折线图并保存为图片if err := createLineChart(lineChartFile); err != nil {log.Fatalf("创建图表时出错: %v", err)}// 在指定标签处插入图表if err := insertImageAt(doc, lineChartFile, "{{tubiao}}"); err != nil {log.Fatalf("插入图表时出错: %v", err)}// 创建柱状图并保存为图片if err := createBarChart(barChartFile); err != nil {log.Fatalf("创建柱状图时出错: %v", err)}// 在指定标签处插入柱状图if err := insertImageAt(doc, barChartFile, "{{zhuzhuangtu}}"); err != nil {log.Fatalf("插入柱状图时出错: %v", err)}// 创建饼图并保存为图片if err := createPieChart(pieChartFile); err != nil {log.Fatalf("创建饼图时出错: %v", err)}// 在指定标签处插入饼图if err := insertImageAt(doc, pieChartFile, "{{bingtu}}"); err != nil {log.Fatalf("插入饼图时出错: %v", err)}// 删除{{a}}到{{b}}之间的段落if err := removeParagraphsBetweenTags(doc, "{{a}}", "{{b}}"); err != nil {log.Fatalf("删除段落时出错: %v", err)}// 删除指定标签if err := removeParagraphWithTag(doc, "{{shanchu}}"); err != nil {log.Fatalf("删除指定标签时出错: %v", err)}// 保存更新后的Word文档if err := doc.SaveToFile(updatedDocPath); err != nil {log.Fatalf("无法保存文档: %v", err)}fmt.Println("文档更新成功")
}// 打开文档
func openDocument(path string) (*document.Document, error) {return document.Open(path) // 使用unioffice库打开指定路径的文档
}// 填充模板中的变量
func fillTemplate(doc *document.Document, replacements map[string]string) {for _, para := range doc.Paragraphs() { // 遍历文档中的每个段落for _, run := range para.Runs() { // 遍历段落中的每个运行(文本片段)text := run.Text()for placeholder, replacement := range replacements { // 遍历需要替换的占位符if strings.Contains(text, placeholder) { // 如果文本包含占位符text = strings.ReplaceAll(text, placeholder, replacement) // 替换占位符run.Clear() // 清除原有内容run.AddText(text) // 添加替换后的文本}}}}
}// 在指定标签处插入表格
func insertTableAt(doc *document.Document, tag string) error {paras := doc.Paragraphs() // 获取文档中的所有段落for _, para := range paras {if paraContainsTag(¶, tag) { // 如果段落包含指定标签// 创建并配置表格table := doc.InsertTableAfter(para) // 在标签段落之后插入表格table.Properties().SetWidthPercent(100) // 设置表格宽度为100%borders := table.Properties().Borders()borders.SetAll(wml.ST_BorderSingle, color.Black, measurement.Dxa) // 设置所有边框为单线黑色for i := 0; i < 3; i++ { // 创建表格行和单元格row := table.AddRow()for j := 0; j < 3; j++ {cell := row.AddCell()cellPara := cell.AddParagraph()cellRun := cellPara.AddRun()cellRun.AddText(fmt.Sprintf("单元格 %d-%d", i+1, j+1))}}//分隔不同表格//doc.InsertParagraphAfter(para).AddRun().AddText("--------------")doc.InsertParagraphAfter(para).AddRun()// 创建并配置表格table = doc.InsertTableAfter(para) // 在标签段落之后插入表格table.Properties().SetWidthPercent(100) // 设置表格宽度为100%borders = table.Properties().Borders()borders.SetAll(wml.ST_BorderSingle, color.Black, measurement.Dxa) // 设置所有边框为单线黑色for i := 0; i < 3; i++ { // 创建表格行和单元格row := table.AddRow()for j := 0; j < 3; j++ {cell := row.AddCell()cellPara := cell.AddParagraph()cellRun := cellPara.AddRun()cellRun.AddText(fmt.Sprintf("单元格 %d-%d", i+1, j+1))}}// 移除标签段落replaceParagraphWithTable(¶, tag) // 替换标签段落为表格// 删除段落doc.RemoveParagraph(para) // 从文档中删除标签段落return nil}}return fmt.Errorf("未找到标签 %s", tag) // 如果未找到标签段落,返回错误
}// 在指定标签处插入图表
func insertImageAt(doc *document.Document, imagePath string, tag string) error {paras := doc.Paragraphs() // 获取文档中的所有段落for _, para := range paras {if paraContainsTag(¶, tag) { // 如果段落包含指定标签img, err := common.ImageFromFile(imagePath) // 从文件中加载图片if err != nil {return fmt.Errorf("无法从文件中加载图片: %v", err)}// 创建图片引用iref, err := doc.AddImage(img) // 将图片添加到文档中if err != nil {return fmt.Errorf("无法将图片添加到文档: %v", err)}// 创建新的段落和运行以插入图表newPara := doc.InsertParagraphAfter(para) // 在标签段落之后插入新段落run := newPara.AddRun()// 插入图片到文档imgInl, err := run.AddDrawingInline(iref) // 在运行中添加图片if err != nil {return fmt.Errorf("插入图片时出错: %v", err)}imgInl.SetSize(6*measurement.Inch, 4*measurement.Inch) // 设置图片尺寸为6x4英寸// 移除标签段落replaceParagraphWithTable(¶, tag) // 替换标签段落为图表// 删除段落doc.RemoveParagraph(para) // 从文档中删除标签段落return nil}}return fmt.Errorf("未找到标签 %s", tag) // 如果未找到标签段落,返回错误
}// 判断段落是否包含指定标签
func paraContainsTag(para *document.Paragraph, tag string) bool {for _, run := range para.Runs() { // 遍历段落中的每个运行if strings.Contains(run.Text(), tag) { // 如果运行文本包含标签return true}}return false
}// 移除标签段落
func replaceParagraphWithTable(para *document.Paragraph, tag string) {// 找到标签的 Runfor _, run := range para.Runs() {log.Printf("替换标签:tag = %v", tag)if strings.Contains(run.Text(), tag) {para.InsertRunAfter(para.AddRun())run.Clear() // 清除原有内容para.RemoveRun(run) // 移除运行break}}
}// 删除两个标签之间的段落
func removeParagraphsBetweenTags(doc *document.Document, startTag, endTag string) error {paras := doc.Paragraphs()startIndex, endIndex := -1, -1// 找到包含startTag和endTag的段落索引for i, para := range paras {if paraContainsTag(¶, startTag) {startIndex = i}if paraContainsTag(¶, endTag) {endIndex = ibreak}}if startIndex == -1 {return fmt.Errorf("未找到标签 %s", startTag)}if endIndex == -1 {return fmt.Errorf("未找到标签 %s", endTag)}if startIndex >= endIndex {return fmt.Errorf("标签 %s 和 %s 之间的顺序不正确", startTag, endTag)}// 删除startTag和endTag之间的段落for i := startIndex; i <= endIndex; i++ {doc.RemoveParagraph(paras[i])}return nil
}// 删除指定标签段落
func removeParagraphWithTag(doc *document.Document, tag string) error {paras := doc.Paragraphs()for _, para := range paras {if paraContainsTag(¶, tag) {doc.RemoveParagraph(para)return nil}}return fmt.Errorf("未找到标签 %s", tag)
}// 创建折线图并保存为图片
func createLineChart(filename string) error {const (width = 900 // 画布宽度height = 600 // 画布高度)// 数据data := []struct {label stringvalue float64}{{"身份证", 15},{"电话号码", 20},{"地址信息", 25},{"银行卡号", 30},{"财务数据", 35},{"基础信息", 40},{"户籍信息", 45},}// 创建画布dc := gg.NewContext(width, height)dc.SetRGB(1, 1, 1) // 背景色为白色dc.Clear()barWidth := float64(width-200) / float64(len(data)) // 调整每个数据点之间的间隔maxValue := 50.0 // 纵坐标最大值// 加载自定义字体if err := dc.LoadFontFace("_examples/word-template/msyh.ttf", 12); err != nil {return fmt.Errorf("无法加载字体: %v", err)}// 绘制折线图dc.SetRGB(0, 0, 0) // 线条颜色为黑色for i, d := range data {x := 50 + float64(i)*(barWidth+20) // x轴起始位置y := height - 50 - (d.value / maxValue * (height - 100)) // y轴起始位置减去数据点的高度if i == 0 {dc.MoveTo(x, y)} else {dc.LineTo(x, y)}}dc.Stroke()// 绘制坐标轴dc.SetRGB(0, 0, 0) // 黑色dc.DrawLine(50, height-50, width-50, height-50) // X轴dc.DrawLine(50, height-50, 50, 50) // Y轴dc.Stroke()// 添加横坐标标签for i, d := range data {dc.DrawStringAnchored(d.label, 50+float64(i)*(barWidth+20), height-30, 0.5, 1)}// 添加纵坐标标签for i := 0; i <= int(maxValue); i += 5 {y := height - 50 - (float64(i) / maxValue * (height - 100))dc.DrawStringAnchored(fmt.Sprintf("%d", int(i)), 30, y, 1, 0.5)}// 保存图像为PNG文件return dc.SavePNG(filename)
}// 创建柱状图并保存为图片
func createBarChart(filename string) error {const (width = 900 // 画布宽度height = 700 // 画布高度)// 数据data := []struct {label stringvalue float64}{{"身份证", 15},{"电话号码", 20},{"地址信息", 25},{"银行卡号", 30},{"财务数据", 35},{"基础信息", 40},{"户籍信息", 45},}// 创建画布dc := gg.NewContext(width, height)dc.SetRGB(1, 1, 1) // 背景色为白色dc.Clear()barWidth := float64(width-200) / float64(len(data)) // 调整柱子的宽度barSpacing := 10.0 // 增加柱子之间的间隔maxValue := 50.0 // 纵坐标最大值// 定义颜色colors := []struct{ R, G, B float64 }{{0.8, 0.2, 0.2}, // 红色{0.2, 0.8, 0.2}, // 绿色{0.2, 0.2, 0.8}, // 蓝色{0.8, 0.8, 0.2}, // 黄色{0.8, 0.2, 0.8}, // 紫色{0.2, 0.8, 0.8}, // 青色{0.8, 0.8, 0.8}, // 灰色}// 加载自定义字体if err := dc.LoadFontFace("_examples/word-template/msyh.ttf", 12); err != nil {return fmt.Errorf("无法加载字体: %v", err)}// 绘制柱状图for i, d := range data {x := 50 + float64(i)*(barWidth+barSpacing) // x轴起始位置y := height - 50 - (d.value / maxValue * (height - 100)) // y轴起始位置减去柱子的高度color := colors[i%len(colors)] // 循环使用颜色dc.SetRGB(color.R, color.G, color.B)dc.DrawRectangle(x, y, barWidth, (d.value / maxValue * (height - 100)))dc.Fill()}// 绘制坐标轴dc.SetRGB(0, 0, 0) // 黑色dc.DrawLine(50, height-50, width-50, height-50) // X轴dc.DrawLine(50, height-50, 50, 50) // Y轴dc.Stroke()// 添加横坐标标签for i, d := range data {dc.DrawStringAnchored(d.label, 50+float64(i)*(barWidth+barSpacing)+barWidth/2, height-30, 0.5, 1)}// 添加纵坐标标签for i := 0; i <= int(maxValue); i += 5 {y := height - 50 - (float64(i) / maxValue * (height - 100))dc.DrawStringAnchored(fmt.Sprintf("%d", int(i)), 30, y, 1, 0.5)}// 保存图像为PNG文件return dc.SavePNG(filename)
}// 创建饼图并保存为图片
func createPieChart(filename string) error {const (width = 640height = 600radius = 200 // 半径)dc := gg.NewContext(width, height)dc.SetRGB(1, 1, 1) // 背景色为白色dc.Clear()data := []struct {label stringvalue float64}{{"身份证", 15}, {"电话号码", 20}, {"地址信息", 25}, {"银行卡号", 30}, {"财务数据", 35}, {"基础信息", 40}, {"户籍信息", 45},}colors := []struct{ R, G, B float64 }{{0.9, 0.3, 0.3}, // 红色{0.3, 0.9, 0.3}, // 绿色{0.3, 0.3, 0.9}, // 蓝色{0.9, 0.9, 0.3}, // 黄色{0.9, 0.3, 0.9}, // 紫色{0.3, 0.9, 0.9}, // 青色{0.9, 0.6, 0.3}, // 橙色}// 设置字体(选择支持中文的字体)if err := dc.LoadFontFace("_examples/word-template/msyh.ttf", 12); err != nil {return fmt.Errorf("无法加载字体: %v", err)}var total float64for _, d := range data {total += d.value}startAngle := -math.Pi / 2 // 从顶部开始绘制for i, d := range data {percentage := d.value / totalangle := percentage * 2 * math.Pi// 设置扇形颜色color := colors[i%len(colors)]dc.SetRGB(color.R, color.G, color.B)dc.DrawArc(width/2, height/2, radius, startAngle, startAngle+angle)dc.LineTo(width/2, height/2)dc.Fill()// 计算标签位置midAngle := startAngle + angle/2labelX := width/2 + (radius+20)*math.Cos(midAngle)labelY := height/2 + (radius+20)*math.Sin(midAngle)// 绘制标签和数据labelText := fmt.Sprintf("%s: %.0f", d.label, d.value)dc.SetRGB(0, 0, 0) // 文字颜色(黑色)dc.DrawStringAnchored(labelText, labelX, labelY, 0.5, 0.5)startAngle += angle}return dc.SavePNG(filename) // 保存图像为PNG文件
}
解释
-
打开文档:
doc, err := openDocument(docPath)
使用
unioffice
库打开指定路径的Word文档。 -
填充模板中的变量:
fillTemplate(doc, map[string]string{"{{TASK_NAME}}": "任务名称示例","{{DETAILS}}": "详细信息示例", })
遍历文档中的每个段落和运行(文本片段),如果文本包含占位符,将其替换为实际值。
-
在指定标签处插入表格:
if err := insertTableAt(doc, "{{biaoge}}"); err != nil {log.Fatalf("插入表格时出错: %v", err) }
查找包含指定标签的段落,在该段落之后插入表格并删除标签段落。
-
创建折线图并保存为图片:
if err := createLineChart(chartFile); err != nil {log.Fatalf("创建图表时出错: %v", err) }
使用
gg
库绘制折线图并保存为PNG文件。 -
在指定标签处插入图表:
if err := insertImageAt(doc, chartFile, "{{tubiao}}"); err != nil {log.Fatalf("插入图表时出错: %v", err) }
查找包含指定标签的段落,在该段落之后插入图表并删除标签段落。
-
保存更新后的文档:
if err := doc.SaveToFile(updatedDocPath); err != nil {log.Fatalf("无法保存文档: %v", err) }
将更新后的文档保存到指定路径。
运行代码
确保您已正确安装所需的库,并将代码中的文件路径替换为您自己的路径。运行代码后,您将得到一个填充了模板变量、插入了表格和图表的更新Word文档。
Word模板
标签填充
张三:{{TASK_NAME}}
李四:{{DETAILS}}插入表格
表格
{{biaoge}}插入图表
图表
{{tubiao}}
{{zhuzhuangtu}}
{{bingtu}}删除标签之间段落
{{a}}
段落1
段落2
段落3
段落4
{{b}}
段落5
删除指定标签
删除指定标签
{{shanchu}}
其他段落
9282892890
拷贝到Word文档中保存为template.docx,长这样:
代码处理后的Word内容
优化版源码
优化版本,图片不保存到磁盘,保存到了缓存。
package mainimport ("bytes""fmt""log""math""strings""github.com/Esword618/unioffice/color""github.com/Esword618/unioffice/common""github.com/Esword618/unioffice/document""github.com/Esword618/unioffice/measurement""github.com/Esword618/unioffice/schema/soo/wml""github.com/fogleman/gg"
)//教程:https://blog.csdn.net/u011019141/article/details/140788882func main() {// 定义文档路径和图表文件路径docPath := "_examples/word-template/template.docx"updatedDocPath := "/Users/songfayuan/Downloads/updated_demo.docx"// 打开文档doc, err := openDocument(docPath)if err != nil {log.Fatalf("无法打开文档: %v", err)}// 填充模板中的变量fillTemplate(doc, map[string]string{"{{TASK_NAME}}": "任务名称示例","{{DETAILS}}": "详细信息示例",})// 在指定标签处插入表格if err := insertTableAt(doc, "{{biaoge}}"); err != nil {log.Fatalf("插入表格时出错: %v", err)}// 创建折线图并存储到缓存中lineChartBuffer, err := createLineChart()if err != nil {log.Fatalf("创建图表时出错: %v", err)}// 在指定标签处插入图表if err := insertImageAt(doc, lineChartBuffer, "{{tubiao}}"); err != nil {log.Fatalf("插入图表时出错: %v", err)}// 创建柱状图并存储到缓存中barChartBuffer, err := createBarChart()if err != nil {log.Fatalf("创建柱状图时出错: %v", err)}// 在指定标签处插入柱状图if err := insertImageAt(doc, barChartBuffer, "{{zhuzhuangtu}}"); err != nil {log.Fatalf("插入柱状图时出错: %v", err)}// 创建饼图并存储到缓存中pieChartBuffer, err := createPieChart()if err != nil {log.Fatalf("创建饼图时出错: %v", err)}// 在指定标签处插入饼图if err := insertImageAt(doc, pieChartBuffer, "{{bingtu}}"); err != nil {log.Fatalf("插入饼图时出错: %v", err)}// 删除{{a}}到{{b}}之间的段落if err := removeParagraphsBetweenTags(doc, "{{a}}", "{{b}}"); err != nil {log.Fatalf("删除段落时出错: %v", err)}// 删除指定标签if err := removeParagraphWithTag(doc, "{{shanchu}}"); err != nil {log.Fatalf("删除指定标签时出错: %v", err)}// 保存更新后的Word文档if err := doc.SaveToFile(updatedDocPath); err != nil {log.Fatalf("无法保存文档: %v", err)}fmt.Println("文档更新成功")
}// 打开文档
func openDocument(path string) (*document.Document, error) {return document.Open(path) // 使用unioffice库打开指定路径的文档
}// 填充模板中的变量
func fillTemplate(doc *document.Document, replacements map[string]string) {for _, para := range doc.Paragraphs() { // 遍历文档中的每个段落for _, run := range para.Runs() { // 遍历段落中的每个运行(文本片段)text := run.Text()for placeholder, replacement := range replacements { // 遍历需要替换的占位符if strings.Contains(text, placeholder) { // 如果文本包含占位符text = strings.ReplaceAll(text, placeholder, replacement) // 替换占位符run.Clear() // 清除原有内容run.AddText(text) // 添加替换后的文本}}}}
}// 在指定标签处插入表格
func insertTableAt(doc *document.Document, tag string) error {paras := doc.Paragraphs() // 获取文档中的所有段落for _, para := range paras {if paraContainsTag(¶, tag) { // 如果段落包含指定标签// 创建并配置表格table := doc.InsertTableAfter(para) // 在标签段落之后插入表格table.Properties().SetWidthPercent(100) // 设置表格宽度为100%borders := table.Properties().Borders()borders.SetAll(wml.ST_BorderSingle, color.Black, measurement.Dxa) // 设置所有边框为单线黑色for i := 0; i < 3; i++ { // 创建表格行和单元格row := table.AddRow()for j := 0; j < 3; j++ {cell := row.AddCell()cellPara := cell.AddParagraph()cellRun := cellPara.AddRun()cellRun.AddText(fmt.Sprintf("单元格 %d-%d", i+1, j+1))}}//分隔不同表格//doc.InsertParagraphAfter(para).AddRun().AddText("--------------")doc.InsertParagraphAfter(para).AddRun()// 创建并配置表格table = doc.InsertTableAfter(para) // 在标签段落之后插入表格table.Properties().SetWidthPercent(100) // 设置表格宽度为100%borders = table.Properties().Borders()borders.SetAll(wml.ST_BorderSingle, color.Black, measurement.Dxa) // 设置所有边框为单线黑色for i := 0; i < 3; i++ { // 创建表格行和单元格row := table.AddRow()for j := 0; j < 3; j++ {cell := row.AddCell()cellPara := cell.AddParagraph()cellRun := cellPara.AddRun()cellRun.AddText(fmt.Sprintf("单元格 %d-%d", i+1, j+1))}}// 移除标签段落replaceParagraphWithTable(¶, tag) // 替换标签段落为表格// 删除段落doc.RemoveParagraph(para) // 从文档中删除标签段落return nil}}return fmt.Errorf("未找到标签 %s", tag) // 如果未找到标签段落,返回错误
}// 在指定标签处插入图表
func insertImageAt(doc *document.Document, imageBuffer *bytes.Buffer, tag string) error {paras := doc.Paragraphs() // 获取文档中的所有段落for _, para := range paras {if paraContainsTag(¶, tag) { // 如果段落包含指定标签img, err := common.ImageFromBytes(imageBuffer.Bytes()) // 从内存缓冲区中加载图片if err != nil {return fmt.Errorf("无法从内存缓冲区中加载图片: %v", err)}// 创建图片引用iref, err := doc.AddImage(img) // 将图片添加到文档中if err != nil {return fmt.Errorf("无法将图片添加到文档: %v", err)}// 创建新的段落和运行以插入图表newPara := doc.InsertParagraphAfter(para) // 在标签段落之后插入新段落run := newPara.AddRun()// 插入图片到文档imgInl, err := run.AddDrawingInline(iref) // 在运行中添加图片if err != nil {return fmt.Errorf("插入图片时出错: %v", err)}imgInl.SetSize(6*measurement.Inch, 4*measurement.Inch) // 设置图片尺寸为6x4英寸// 移除标签段落replaceParagraphWithTable(¶, tag) // 替换标签段落为图表// 删除段落doc.RemoveParagraph(para) // 从文档中删除标签段落return nil}}return fmt.Errorf("未找到标签 %s", tag) // 如果未找到标签段落,返回错误
}// 判断段落是否包含指定标签
func paraContainsTag(para *document.Paragraph, tag string) bool {for _, run := range para.Runs() { // 遍历段落中的每个运行if strings.Contains(run.Text(), tag) { // 如果运行文本包含标签return true}}return false
}// 移除标签段落
func replaceParagraphWithTable(para *document.Paragraph, tag string) {// 找到标签的 Runfor _, run := range para.Runs() {log.Printf("替换标签:tag = %v", tag)if strings.Contains(run.Text(), tag) {para.InsertRunAfter(para.AddRun())run.Clear() // 清除原有内容para.RemoveRun(run) // 移除运行break}}
}// 删除两个标签之间的段落
func removeParagraphsBetweenTags(doc *document.Document, startTag, endTag string) error {paras := doc.Paragraphs()startIndex, endIndex := -1, -1// 找到包含startTag和endTag的段落索引for i, para := range paras {if paraContainsTag(¶, startTag) {startIndex = i}if paraContainsTag(¶, endTag) {endIndex = ibreak}}if startIndex == -1 {return fmt.Errorf("未找到标签 %s", startTag)}if endIndex == -1 {return fmt.Errorf("未找到标签 %s", endTag)}if startIndex >= endIndex {return fmt.Errorf("标签 %s 和 %s 之间的顺序不正确", startTag, endTag)}// 删除startTag和endTag之间的段落for i := startIndex; i <= endIndex; i++ {doc.RemoveParagraph(paras[i])}return nil
}// 删除指定标签段落
func removeParagraphWithTag(doc *document.Document, tag string) error {paras := doc.Paragraphs()for _, para := range paras {if paraContainsTag(¶, tag) {doc.RemoveParagraph(para)return nil}}return fmt.Errorf("未找到标签 %s", tag)
}// 创建折线图并将其存储到缓存中
func createLineChart() (*bytes.Buffer, error) {const (width = 900 // 画布宽度height = 600 // 画布高度)// 数据data := []struct {label stringvalue float64}{{"身份证", 15},{"电话号码", 20},{"地址信息", 25},{"银行卡号", 30},{"财务数据", 35},{"基础信息", 40},{"户籍信息", 45},}// 创建画布dc := gg.NewContext(width, height)dc.SetRGB(1, 1, 1) // 背景色为白色dc.Clear()barWidth := float64(width-200) / float64(len(data)) // 调整每个数据点之间的间隔maxValue := 50.0 // 纵坐标最大值// 加载自定义字体if err := dc.LoadFontFace("_examples/word-template/msyh.ttf", 12); err != nil {return nil, nil}// 绘制折线图dc.SetRGB(0, 0, 0) // 线条颜色为黑色for i, d := range data {x := 50 + float64(i)*(barWidth+20) // x轴起始位置y := height - 50 - (d.value / maxValue * (height - 100)) // y轴起始位置减去数据点的高度if i == 0 {dc.MoveTo(x, y)} else {dc.LineTo(x, y)}}dc.Stroke()// 绘制坐标轴dc.SetRGB(0, 0, 0) // 黑色dc.DrawLine(50, height-50, width-50, height-50) // X轴dc.DrawLine(50, height-50, 50, 50) // Y轴dc.Stroke()// 添加横坐标标签for i, d := range data {dc.DrawStringAnchored(d.label, 50+float64(i)*(barWidth+20), height-30, 0.5, 1)}// 添加纵坐标标签for i := 0; i <= int(maxValue); i += 5 {y := height - 50 - (float64(i) / maxValue * (height - 100))dc.DrawStringAnchored(fmt.Sprintf("%d", int(i)), 30, y, 1, 0.5)}// 将图表保存到缓冲区var buf bytes.Bufferif err := dc.EncodePNG(&buf); err != nil {return nil, err}return &buf, nil
}// 创建柱状图并将其存储到缓存中
func createBarChart() (*bytes.Buffer, error) {const (width = 900 // 画布宽度height = 700 // 画布高度)// 数据data := []struct {label stringvalue float64}{{"身份证", 15},{"电话号码", 20},{"地址信息", 25},{"银行卡号", 30},{"财务数据", 35},{"基础信息", 40},{"户籍信息", 45},}// 创建画布dc := gg.NewContext(width, height)dc.SetRGB(1, 1, 1) // 背景色为白色dc.Clear()barWidth := float64(width-200) / float64(len(data)) // 调整柱子的宽度barSpacing := 10.0 // 增加柱子之间的间隔maxValue := 50.0 // 纵坐标最大值// 定义颜色colors := []struct{ R, G, B float64 }{{0.8, 0.2, 0.2}, // 红色{0.2, 0.8, 0.2}, // 绿色{0.2, 0.2, 0.8}, // 蓝色{0.8, 0.8, 0.2}, // 黄色{0.8, 0.2, 0.8}, // 紫色{0.2, 0.8, 0.8}, // 青色{0.8, 0.8, 0.8}, // 灰色}// 加载自定义字体if err := dc.LoadFontFace("_examples/word-template/msyh.ttf", 12); err != nil {return nil, nil}// 绘制柱状图for i, d := range data {x := 50 + float64(i)*(barWidth+barSpacing) // x轴起始位置y := height - 50 - (d.value / maxValue * (height - 100)) // y轴起始位置减去柱子的高度color := colors[i%len(colors)] // 循环使用颜色dc.SetRGB(color.R, color.G, color.B)dc.DrawRectangle(x, y, barWidth, (d.value / maxValue * (height - 100)))dc.Fill()}// 绘制坐标轴dc.SetRGB(0, 0, 0) // 黑色dc.DrawLine(50, height-50, width-50, height-50) // X轴dc.DrawLine(50, height-50, 50, 50) // Y轴dc.Stroke()// 添加横坐标标签for i, d := range data {dc.DrawStringAnchored(d.label, 50+float64(i)*(barWidth+barSpacing)+barWidth/2, height-30, 0.5, 1)}// 添加纵坐标标签for i := 0; i <= int(maxValue); i += 5 {y := height - 50 - (float64(i) / maxValue * (height - 100))dc.DrawStringAnchored(fmt.Sprintf("%d", int(i)), 30, y, 1, 0.5)}// 将图表保存到缓冲区var buf bytes.Bufferif err := dc.EncodePNG(&buf); err != nil {return nil, err}return &buf, nil
}// 创建饼图并将其存储到缓存中
func createPieChart() (*bytes.Buffer, error) {const (width = 640height = 600radius = 200 // 半径)dc := gg.NewContext(width, height)dc.SetRGB(1, 1, 1) // 背景色为白色dc.Clear()data := []struct {label stringvalue float64}{{"身份证", 15}, {"电话号码", 20}, {"地址信息", 25}, {"银行卡号", 30}, {"财务数据", 35}, {"基础信息", 40}, {"户籍信息", 45},}colors := []struct{ R, G, B float64 }{{0.9, 0.3, 0.3}, // 红色{0.3, 0.9, 0.3}, // 绿色{0.3, 0.3, 0.9}, // 蓝色{0.9, 0.9, 0.3}, // 黄色{0.9, 0.3, 0.9}, // 紫色{0.3, 0.9, 0.9}, // 青色{0.9, 0.6, 0.3}, // 橙色}// 设置字体(选择支持中文的字体)if err := dc.LoadFontFace("_examples/word-template/msyh.ttf", 12); err != nil {return nil, nil}var total float64for _, d := range data {total += d.value}startAngle := -math.Pi / 2 // 从顶部开始绘制for i, d := range data {percentage := d.value / totalangle := percentage * 2 * math.Pi// 设置扇形颜色color := colors[i%len(colors)]dc.SetRGB(color.R, color.G, color.B)dc.DrawArc(width/2, height/2, radius, startAngle, startAngle+angle)dc.LineTo(width/2, height/2)dc.Fill()// 计算标签位置midAngle := startAngle + angle/2labelX := width/2 + (radius+20)*math.Cos(midAngle)labelY := height/2 + (radius+20)*math.Sin(midAngle)// 绘制标签和数据labelText := fmt.Sprintf("%s: %.0f", d.label, d.value)dc.SetRGB(0, 0, 0) // 文字颜色(黑色)dc.DrawStringAnchored(labelText, labelX, labelY, 0.5, 0.5)startAngle += angle}// 将图表保存到缓冲区var buf bytes.Bufferif err := dc.EncodePNG(&buf); err != nil {return nil, err}return &buf, nil
}
The end …
相关文章:

Golang处理Word文档模板实现标签填充|表格插入|图标绘制和插入|删除段落|删除标签
本教程主要实现【Golang处理Word文档模板实现标签填充|表格插入|图标绘制和插入|删除段落|删除标签】。 本文源码:https://gitee.com/songfayuan/go-zero-demo 教程源码分支:master 分支(_examples/word-template/fill-word-template.go&…...

PHP学习:PHP基础
以.php作为后缀结尾的文件,由服务器解析和运行的语言。 一、语法 PHP 脚本可以放在文档中的任何位置。 PHP 脚本以 <?php 开始,以 ?> 结束。 <!DOCTYPE html> <html> <body><h1>My first PHP page</h1><?php …...

Xinstall新玩法:Web直接打开App,用户体验再升级!
在移动互联网时代,App已成为我们日常生活中不可或缺的一部分。然而,在App推广和运营过程中,许多开发者面临着从Web端引流到App的难题。这时,Xinstall作为国内专业的App全渠道统计服务商,提供了一种创新的解决方案——通…...

perf 排查高延迟
高延迟的 2 个场景,触发 perf 录包思路 当前 perf 没有常驻内存,后续提供 perf 常驻内存功能。且 perf 启动需要 0.5~1s,所以,存在 2 个场景 1.频繁连续高延迟(复现后的几秒内,继续频繁复现)&a…...

配置8188eu无线网卡的热点模式
下载驱动 github:8188eu的最新驱动,注意下载5.2.2.4分支 关于这一驱动,不要下载master分支,master分支代码较早,会导致以下两点问题: 1.STA模式下连接wifi信号较差时会卡死 2.无法启动AP模式 所以请务必下载5.2.2.4分…...

为什么 DDoS 攻击偏爱使用 TCP 和 UDP 包?
Distributed Denial of Service (DDoS) 攻击是指攻击者利用多个计算机系统或网络设备(通常是被恶意软件感染的计算机,被称为“僵尸网络”)来淹没目标服务器的资源,导致合法用户无法访问服务。TCP 和 UDP 是两种最常见的用于 DDoS …...

多址技术(FDMA,TDMA,CDMA,帧,时隙)(通俗易懂)
多址技术是一种区分用户的技术。 举个例子,一个基站发出信息,如何确定是发给谁的? 这个技术就是解决这个问题的。 多址技术常见的有三种: 频分多址(FDMA)、时分多址(TDMA)、码分…...

基于 KubeSphere 的 Kubernetes 生产环境部署架构设计及成本分析
转载:基于 KubeSphere 的 Kubernetes 生产环境部署架构设计及成本分析 前言 导图 1. 简介 1.1 架构概要说明 今天分享一个实际小规模生产环境部署架构设计的案例,该架构设计概要说明如下: 本架构设计适用于中小规模(<50)的 Kubernetes …...

RabbitMQ 入门篇
接上一篇《RabbitMQ-安装篇(阿里云主机)-CSDN博客》 安装好RabbitMQ后,我们将开始RabbitMQ的使用,根据官网文档RabbitMQ Tutorials | RabbitMQ,我们一步一步的学习。 1. "Hello World!" 这里先说明几个概…...

【赛事】2024第五届“华数杯”全国大学生数学建模竞赛
为了培养学生的创新意识及运用数学方法和计算机技术解决实际问题的能力,天津市未来与预测科学研究会、中国未来研究会大数据与数学模型专业委员会(协办)决定举办华数杯全国大学生数学建模竞赛。竞赛的目标是为培养大学生的科学精神及运用数学…...

DB管理客户端navicat、DBever、DbVisualizer数据库连接信息迁移
DB管理客户端Navicat、DBever、DbVisualizer数据库连接信息迁移 第三方数据库连接工具为了确保数据库信息安全通常对保存的数据库连接密码进行加密,填入后想再拿到原文就不可能了,有时交接给别人或者换电脑时可以通过连接数据导出的方式来解决。 Navic…...

CF 训练2
688 div2 C Balanced Bitstring 思路:首先对于区间问题 , 我们可以先思考让它滑动滑动。对于[l,r],向后滑动一位后 ,[l1 , r1],因为两次的区间中 , [l1 ,r]中所有数都是相同的 , 所以 可以得到s[l] s[r1] ࿰…...

内网隧道学习笔记
1.基础: 一、端口转发和端口映射 1.端口转发是把一个端口的流量转发到另一个端口 2.端口映射是把一个端口映射到另一个端口上 二、http代理和socks代理 1.http带那里用http协议、主要工作在应用层,主要用来代理浏览网页。 2.socks代理用的是socks协议、…...

Umi-OCR:功能强大且易于使用的本地照片识别软件
Umi-OCR是一款开源且免费的离线OCR(光学字符识别)软件,可让您轻松从照片中提取文本。它支持多种语言,并具有许多其他功能使其成为照片识别任务的绝佳选择。 Umi-OCR的优势 离线操作: Umi-OCR无需互联网连接即可工作&…...

HarmonyOS开发商城商品详情-底部导航
目录 一:功能概述 二:代码实现 三:效果图 一:功能概述 上一节我们实现了商品详情页基础信息展示,这一节主要实现底部立即购买和加入购物车的功能。首先我们需要在底部创建两个按钮,这两个按钮固定字底部,不随页面滚动。点击添加购物车按钮,会出现一个对话框,显示商…...

C语言 ——— 学习、使用 strcat函数 并模拟实现
目录 学习strcat函数编辑 使用strcat函数编辑 模拟实现strcat函数 学习strcat函数 strcat函数所需要的头文件: #include<string.h> strcat函数的参数解析: 将 source 字符串追加到 destination 字符串。destination 中的字符串结束标志…...

视频超压缩保持质量 ffmpeg
参考: https://x.com/mortenjust/status/1817991110544744764 基于 FFMpeg 的 H264 压缩标准,实现压缩 90% 的视频大小 在线体验地址: https://tools.rotato.app/compress ffmpeg命令执行 ffmpeg -i "C:\Users\loong\Downloads\屏幕录…...

大型语言模型入门
大型语言模型ChatGPT 快速、全面了解大型语言模型。学习李宏毅课程笔记。 ChatGPT 目前由OpenAI公司发明的非常火的人工智能AI应用ChatGPT,到底是什么原理呢? G:Generative(生成) P:Pre-trained(预训练) T:Transform…...

canvas-视频绘制
通过Canvas元素来实时绘制一个视频帧,并在视频帧上叠加一个图片的功能可以当作水印。 获取Canvas元素: let canvas document.getElementById(canvas) 通过getElementById函数获取页面中ID为canvas的Canvas元素,并将其存储在变量canvas中。 …...

红酒与美食搭配:味觉的新探索
在美食的世界里,红酒如同一位优雅的舞者,与各种佳肴共舞,创造出无尽的味觉惊喜。当定制红酒洒派红酒(Bold & Generous)与各式美食相遇,便开启了一场味觉的新探索之旅。 一、红酒与美食的邂逅ÿ…...

大模型日报 2024-08-02
大模型日报 2024-08-02 大模型资讯 博思艾伦在国际空间站部署先进语言模型 摘要: 博思艾伦在国际空间站上的超级计算机上运行了一种生成式人工智能大型语言模型。这一举措标志着语言模型在太空应用方面的重大进展。 人工智能助力研发安全有效的新型抗生素对抗耐药细菌 摘要: 德…...

【Pytorch】一文向您详细介绍 torch.sign()
🎉🧠**【Pytorch】一文向您详细介绍 torch.sign()** 下滑即可查看博客内容 🌈 欢迎莅临我的个人主页 👈这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地!🎇 🎓 博主简介ÿ…...

超级详细,如何手动安装python第三方库?
文章目录 1,python第三方库安装包有3种类型2,python第三方库安装包whl文件如何安装?3,python第三方库安装包zip和tar.gz文件如何安装?4, python第三方库安装包exe文件如何安装? 手动安装第三方库…...

WebSocket协议测试
WebSocket和HTTP接口有什么不一样 websocket和http都是网络接口数据交换的协议。都是基于TCP 协议区别 http:每次数据交互都是一个全新的请求;主动发起http请求调用(非实时) websocket:建立长久网络连接,服务器/客户端可以相互主动发数据…...

浅谈【C#】代码注册COM组件
在C#中注册COM组件通常涉及到使用regasm工具或者在代码中使用System.Runtime.InteropServices命名空间下的RegisterTypeForComClients方法。 下面是两种方法的简要说明和示例: 1、使用 regasm 工具 regasm 是一个命令行工具,用于将.NET程序集注册为CO…...

C++数据结构学习(顺序表)
文章目录 顺序表杭州电子科技大学在线评测2008 数值统计使用顺序表实现 2014 青年歌手大奖赛_评委会打分 Leetcode题目[LCP 01. 猜数字](https://leetcode.cn/problems/guess-numbers/description/)[LCP 06. 拿硬币](https://leetcode.cn/problems/na-ying-bi/description/)[20…...

springboot宠物用品商城系统-前端-计算机毕业设计源码74346
摘要 基于微信小程序的宠物用品商城系统是一个集商品展示、在线购物、支付结算、用户管理等功能于一体的综合性电商平台。该系统充分利用微信小程序的便捷性和用户基础,为宠物爱好者提供了一个方便、快捷的购物体验。 同时,该系统还具备完善的用户管理功…...

【vue预览PDF文件的几种方法】
vue展示PDF文件的几种方法 使用Vue插件 你需要安装vue-pdf-embed: npm install vue-pdf-embed<template><div class"pdf-container"><VuePdfEmbed :src"pdfUrl" /></div> </template><script setup lang"ts"…...

学习安卓开发遇到的问题(未解决版,有没有人帮我看看,大哭,感谢)
问题1:学习禁用与恢复按钮中: java代码报错:报错代码是 R.id.btn_enable;case R.id.btn_disable;case R.id.btn_test: 代码如下:(实现功能在代码后面) package com.example.apptest;import static java.…...

C++必修:STL之vector的模拟实现
✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C学习 贝蒂的主页:Betty’s blog 为了让我们更加深入理解vector,接下来我们将模拟实现一个简易版的vect…...