gitbrent / PptxGenJS

Create PowerPoint presentations with a powerful, concise JavaScript API.
https://gitbrent.github.io/PptxGenJS/
MIT License
2.84k stars 625 forks source link

[BUG]As soon as the composite chart is used, the repair will pop up when opening the PPT with office #1233

Open Macxx-could opened 1 year ago

Macxx-could commented 1 year ago

Issue Category

Product Versions

Desired Behavior

  1. Open PPT normally without any pop-up box
  2. Composite charts display normally

Observed Behavior

Pop-up repair when opening PPT

25A68C38-9B73-445C-8A32-B5805041D9D3

Click Repair, enter the PPT, the page with the chart becomes a blank page

8024D6DB-0759-40BB-9E65-E31645CF94F9

If the code related to the chart is commented, this problem will not occur

Note that not all composite charts will have this problem, and composite charts on some pages can be displayed normally

Steps to Reproduce

    const leftTypes = [
        {
            "type": "bar",
            "data": [
                {
                    "name": "权责使用率",
                    "labels": ["数字化", "公共", "营销管理", "合同与付款", "产品设计", "成本管理", "费用报销", "招采管理", "人力资源", "财务金融", "行政办公", "客户关系", "运营管理", "工程管理", "安全", "物业管理"],
                    "values": ["100", "100", "90.9", "80.6", "75", "73.3", "66.7", "64.2", "61.5", "57.1", "53.8", "33.3", "26.7", "16.7", "16.7", "0"]
                }
            ],
            "options": {
                "chartColors": [
                    "599ad5"
                ],
                "barGrouping": "stacked",
                "showValue": true,
                "showLabel": true,
                "barGapWidthPct": 250
            }
        },
        {
            "type": "line",
            "data": [
                {
                    "name": "23年非标使用率",
                    "labels": ["数字化", "公共", "营销管理", "合同与付款", "产品设计", "成本管理", "费用报销", "招采管理", "人力资源", "财务金融", "行政办公", "客户关系", "运营管理", "工程管理", "安全", "物业管理"],
                    "values": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]
                }
            ],
            "options": {
                "chartColors": [
                    "ffc000"
                ],
                "secondaryValAxis": true,
                "secondaryCatAxis": true,
                "showValue": true,
                "showLabel": true,
                "lineDataSymbolSize": 1
            }
        }
    ]
const props ={
    "x": 2.066928675,
    "y": 1.476377625,
    "w": 11.059052663000001,
    "h": 2.685038774,
    "chartArea": {
        "fill": {
            "color": "ffffff"
        }
    },
    "barDir": "col",
    "barGrouping": "stacked",
    "dataLabelPosition": "t",
    "dataLabelFormatCode": "0.0\"%\"",
    "dataLabelFontFace": "微软雅黑",
    "dataLabelFontSize": 8,
    "catAxisLabelColor": "494949",
    "catAxisLabelFontFace": "微软雅黑",
    "catAxisLabelFontSize": 8,
    "catAxisLabelRotate": 0,
    "showLegend": true,
    "legendPos": "b",
    "showTitle": true,
    "titleFontFace": "微软雅黑",
    "titleFontSize": 12,
    "title": "华东区域公司权责使用率(累计23年)",
    "valAxes": [
        {
            "showValAxisTitle": false,
            "valAxisHidden": true,
            "valAxisLineShow": false,
            "valGridLine": {
                "style": "none"
            }
        },
        {
            "showValAxisTitle": false,
            "valAxisHidden": true,
            "valAxisLineShow": false,
            "valGridLine": {
                "style": "none"
            }
        }
    ],
    "catAxes": [
        {
            "catAxisTitle": "Year"
        },
        {
            "catAxisHidden": true
        }
    ]
}
slide.addChart(leftTypes, {
        ...props,
        title: `地产权责使用率(累计2022年)`,
    });
Macxx-could commented 1 year ago

Based on the demo code on the official website, I gradually realized my own needs. I observed that the problem of popping up the repair box appeared after that step of operation, and then located the problem.

1. Run the demo code directly

⭐Result: The export is normal, the opening is normal, and the repair box does not pop up.

2. Replace demo data with backend data

Replace the chart data in the demo code with the chart data obtained from the backend.

⭐Result: The export is normal, the opening is normal, and the repair box does not pop up

This step ruled out the problem of the data format. I began to guess whether it was a problem with the configuration item, so I started to check the configuration item.

3. valAxes

Since my chart doesn't need to display the y-axis on both sides, I changed this configuration to the following:

// before
valAxes: [
    {
        showValAxisTitle: true,
        valAxisTitle: "Cars Produced (m)",
        valAxisMaxVal: 10,
        valAxisTitleColor: "1982c4",
        valAxisLabelColor: "1982c4",
    },
    {
        showValAxisTitle: true,
        valAxisTitle: "Global Market Share (%)",
        valAxisMaxVal: 10,
        valAxisTitleColor: "F38940",
        valAxisLabelColor: "F38940",
        valGridLine: { style: "none" },
    },
],
// after
const valAxesItem = {
    showValAxisTitle: false,
    valAxisHidden: true,
    valAxisLineShow: false,
    valGridLine: { style: 'none' }
}
valAxes: [valAxesItem, valAxesItem]

⭐Result: The export is normal, the opening is normal, and the repair box does not pop up

4. dataLabel

Because my chart needs to display the data label of each data point, I made the following configuration:

const comboProps = {
    chartArea: { fill: { color: "FFFFFF" } },
    barDir: "col",
    // barGrouping: "stacked", // delete this line
    // 👇
    showValue: true,
    showLabel: true,
    dataLabelPosition: 't',
    dataLabelFormatCode: '0.0"%"',
    dataLabelFontFace: "微软雅黑",
    dataLabelFontSize: 8,
    // 👆
    catAxisLabelColor: "494949",
    // ...more properties
}

⚠Result: The export is normal, and the pop-up repair box is opened.

Then I will add these lines of code and comment them line by line:

So I came to the source code file pptgenjs/dist/pptxgenjs.es.js to search for dataLabelPosition, and came to the 1815th line of code in the file, and checked the instructions in the REFERENCE URL left in the code

https://docs.microsoft.com/en-us/openspecs/office_standards/ms-oi29500/e2b1697c-7adc-463d-9081-3daef72f656f?redirectedfrom=MSDN

learned that

image-20230322104556337

The legal value of dataLabelPosition of the histogram of barGrouping=clustered is one of InsideBase, InsideEnd, OutsideEnd, Center, the author of pptgenjs has processed this value, the code is as follows:

if (options.dataLabelPosition) {
    // ...
    if (options._type === CHART_TYPE.BUBBLE || options._type === CHART_TYPE.BUBBLE3D || options._type === CHART_TYPE.LINE || options._type === CHART_TYPE.SCATTER) {
        if (!['b', 'ctr', 'l', 'r', 't'].includes(options.dataLabelPosition))
            delete options.dataLabelPosition;
    }
    if (options._type === CHART_TYPE.BAR) {
        if (!['stacked', 'percentStacked'].includes(options.barGrouping || '')) {
            if (!['ctr', 'inBase', 'inEnd'].includes(options.dataLabelPosition))
                delete options.dataLabelPosition;
        }
        if (!['clustered'].includes(options.barGrouping || '')) {
            if (!['ctr', 'inBase', 'inEnd', 'outEnd'].includes(options.dataLabelPosition))
                delete options.dataLabelPosition;
        }
    }
}

It can be seen that the author hopes that when the value of dataLabelPosition is not the legal value of dataLabelPosition for a certain type of chart, clear dataLabelPosition and use the default value of dataLabelPosition.

However, through console.log('options._type:', options._type, CHART_TYPE), I found that when we use a composite chart, the value of options._type is an array, and CHART_TYPE.XX The value of is a string, therefore, in the composite chart, the above piece of code cannot clear the illegal value of dataLabelPosition for certain types of charts.

image-20230322111316185

For further verification, add console.log statements in these places of the code

image-20230322112411424

Then execute the operation of generating PPT, and the printed results displayed in the console are as follows

image-20230322112526526

It can be seen that the logic of clearing the value of dataLabelPosition that is not legal is indeed not entered.

So I guess, is it because the value of dataLabelPosition of the histogram is illegal, which caused the repair window to pop up when opening the PPT file? After all, there is a problem with the content displayed in the pop-up window. Isn’t it a problem to use an illegal value?

image-20230322112906975

At first I thought it was a bug, but then I ran to read the official documentation, thought about it carefully, and found that the documentation divides the properties of the chart into

At first, I didn’t think there was any problem with writing the document like this. Now, combined with the above situation, I think the meaning of the document is to tell us that dataLabelPosition should not be placed in general, but should be placed in the separate options of the chart .

// comboTypes
{
    type: ppt.charts.BAR,
    data: [{
        name: config.barName,
        labels: config.labels,
        values: config.barValues,
    }],
    options: { chartColors: [config.barColor], barGapWidthPct: 250, dataLabelPosition: 'outEnd', },
},

Sure enough, I put it here, it exported normally, and no repair window popped up.

5. lineDataSymbolSize

This property will also cause the repair window to pop up, and strangely, the repair window will only pop up when the value of this property is 1.

image-20230322125536694

But in the documentation it shows that the value of this property can be 1-256

Even if I checked the source code, I didn't understand why, so I could only wait for the author's reply.

conclusion

The emergence of this problem is the result of the following points:

  1. The user did not use the library according to the official documentation
  2. The description in the official document is not well prepared. For example, the value of dataLabelPosition has different valid values that can be used in different charts, but there is no explanation in the document, but all the optional values of this attribute are placed. listed together.
  3. There is a problem with the processing logic of the illegal value of options.dataLabelPosition in the code.
Macxx-could commented 1 year ago

demo code path:demos/modules/demo_chart.mjs line:1761-1823

Macxx-could commented 1 year ago

My English is not very good. I translated the above article line by line through Google. The following is my original text. I am sorry for the inconvenience caused.

Macxx-could commented 1 year ago

我以官网demo代码为基础,逐步逐步实现自己的需求,观察是在那一步操作之后出现弹出修复框这个问题,进而定位问题所在。

1. 直接运行demo代码

⭐结果:导出正常,打开正常,未弹出修复框。

2. 用后端数据替换demo数据

用后端获取到的图表数据替换掉demo代码中的图表数据。

⭐结果:导出正常,打开正常,未弹出修复框

这一步排除了数据格式的问题,我开始猜测是否配置项的问题,于是开始排查配置项

3. valAxes

因为我的图表不需要显示两边的y轴,所以我把这个配置改为下面这样:

// before
valAxes: [
    {
        showValAxisTitle: true,
        valAxisTitle: "Cars Produced (m)",
        valAxisMaxVal: 10,
        valAxisTitleColor: "1982c4",
        valAxisLabelColor: "1982c4",
    },
    {
        showValAxisTitle: true,
        valAxisTitle: "Global Market Share (%)",
        valAxisMaxVal: 10,
        valAxisTitleColor: "F38940",
        valAxisLabelColor: "F38940",
        valGridLine: { style: "none" },
    },
],
// after
const valAxesItem = {
    showValAxisTitle: false,
    valAxisHidden: true,
    valAxisLineShow: false,
    valGridLine: { style: 'none' }
}
valAxes: [valAxesItem, valAxesItem]

⭐结果:导出正常,打开正常,未弹出修复框

4. dataLabel

因为我的图表需要把每个数据点的数据标签显示出来,因此我做了如下配置:

const comboProps = {
    chartArea: { fill: { color: "FFFFFF" } },
    barDir: "col",
    // barGrouping: "stacked", // delete this line
    // 👇
    showValue: true,
    showLabel: true,
    dataLabelPosition: 't',
    dataLabelFormatCode: '0.0"%"',
    dataLabelFontFace: "微软雅黑",
    dataLabelFontSize: 8,
    // 👆
    catAxisLabelColor: "494949",
    // ...more properties
}

⚠结果:导出正常,打开弹出修复框。

然后我将新增的这几行代码,逐行注释:

于是我来到源码文件 pptgenjs/dist/pptxgenjs.es.js 中搜索 dataLabelPosition,来到了文件的第1815行代码处,通过查阅代码中留下的 REFERENCE 网址中的说明

https://docs.microsoft.com/en-us/openspecs/office_standards/ms-oi29500/e2b1697c-7adc-463d-9081-3daef72f656f?redirectedfrom=MSDN

得知

image-20230322104556337

barGrouping=clustered 的柱状图的 dataLabelPosition 的合法值为 InsideBase, InsideEnd, OutsideEnd, Center 其中一个,pptgenjs的作者有对这个值进行处理,代码如下:

if (options.dataLabelPosition) {
    // ...
    if (options._type === CHART_TYPE.BUBBLE || options._type === CHART_TYPE.BUBBLE3D || options._type === CHART_TYPE.LINE || options._type === CHART_TYPE.SCATTER) {
        if (!['b', 'ctr', 'l', 'r', 't'].includes(options.dataLabelPosition))
            delete options.dataLabelPosition;
    }
    if (options._type === CHART_TYPE.BAR) {
        if (!['stacked', 'percentStacked'].includes(options.barGrouping || '')) {
            if (!['ctr', 'inBase', 'inEnd'].includes(options.dataLabelPosition))
                delete options.dataLabelPosition;
        }
        if (!['clustered'].includes(options.barGrouping || '')) {
            if (!['ctr', 'inBase', 'inEnd', 'outEnd'].includes(options.dataLabelPosition))
                delete options.dataLabelPosition;
        }
    }
}

可以看得出来,作者希望:当 dataLabelPosition 的值不是某一类图表的 dataLabelPosition 的合法值的时候,就清除掉 dataLabelPosition ,进而采用 dataLabelPosition 的默认值。

但是,我通过 console.log('options._type:', options._type, CHART_TYPE) ,发现,当我们使用复合图表的时候,options._type 的值是一个数组,而 CHART_TYPE.XX 的值是一个字符串,因此,在复合图表中,上面这一段代码并不能清除掉某类图表不合法的 dataLabelPosition 的值。

image-20230322111316185

为了更进一步的验证,在代码的这几个地方加上 console.log 语句

image-20230322112411424

然后执行生成PPT操作,在控制台中的显示的打印结果如下

image-20230322112526526

可以看出,确实没有进入到清除不合法的 dataLabelPosition 的值的逻辑中。

所以我猜测,是否因为柱状图的 dataLabelPosition 的值不合法,才导致了打开PPT文件的时候弹出了修复窗呢?毕竟,弹出窗中显示的是内容有问题,用了不合法的值不就是问题吗?

image-20230322112906975

本来我觉得这是一个bug,但是后来我又跑去看了一下官方的使用文档,仔细想了一下,发现文档将图表的属性分为

一开始没觉得文档这样写有什么问题,现在结合上面的情况,我觉得文档这样写的意思是告诉我们 dataLabelPosition 不要放在 general 中,而是应该放在图表单独的 options 中。

// comboTypes
{
    type: ppt.charts.BAR,
    data: [{
        name: config.barName,
        labels: config.labels,
        values: config.barValues,
    }],
    options: { chartColors: [config.barColor], barGapWidthPct: 250, dataLabelPosition: 'outEnd', },
},

果然,我放在这里,它导出正常,并且没有弹出修复窗口。

5. lineDataSymbolSize

这个属性也会导致弹出修复窗,而且奇怪的是,当这个属性的值为1的时候,才会弹出修复窗。

image-20230322125536694

但是在文档中显示,该属性的值可以是1-256

这个即使查看了源码,我也没想明白为什么,只能等待作者回复了。

结论

这个问题的出现,是以下这几点的作用结果

  1. 用户未按照官方文档来使用该库
  2. 官方文档中描述的不够准备,比如说 dataLabelPosition 的值在不同图表中可以使用的有效值是不一样的,但是文档中并没有说明,而是把这个属性的所有可选值都放在一起列了出来。
  3. 代码中对 options.dataLabelPosition 的非合法值的处理逻辑存在问题。