javascript - pdf.js:获取文本颜色

标签 javascript pdf.js

我有一个简单的 pdf 文件,其中包含单词“Hello world”,每个单词都有不同的颜色。

我正在加载 PDF,如下所示:

PDFJS.getDocument('test.pdf').then( onPDF );

function onPDF( pdf )
{
    pdf.getPage( 1 ).then( onPage );
}

function onPage( page )
{
    page.getTextContent().then( onText );
}

function onText( text )
{   
    console.log( JSON.stringify( text ) );
}

我得到这样的 JSON 输出:

{
    "items" : [{
            "str" : "Hello ",
            "dir" : "ltr",
            "width" : 29.592,
            "height" : 12,
            "transform" : [12, 0, 0, 12, 56.8, 774.1],
            "fontName" : "g_font_1"
        }, {
            "str" : "world",
            "dir" : "ltr",
            "width" : 27.983999999999998,
            "height" : 12,
            "transform" : [12, 0, 0, 12, 86.5, 774.1],
            "fontName" : "g_font_1"
        }
    ],
    "styles" : {
        "g_font_1" : {
            "fontFamily" : "serif",
            "ascent" : 0.891,
            "descent" : 0.216
        }
    }
}

但是,我一直没能找到一种方法来确定每个单词的颜色。当我渲染它时,它会正确渲染,所以我知道信息就在某处。我可以在什么地方访问它吗?

最佳答案

正如 Respawned 所暗示的,没有适用于所有情况的简单答案。话虽这么说,这里有两种方法似乎效果很好。两者都有优点和缺点。

方法一

在内部,getTextContent 方法使用所谓的 EvaluatorPreprocessor 来解析 PDF 运算符,并保持图形状态。所以我们可以做的是,实现一个自定义的EvaluatorPreprocessor,覆盖preprocessCommand方法,并使用它来将当前文本颜色添加到图形状态。一旦就绪,无论何时创建新文本 block ,我们都可以添加颜色属性,并将其设置为当前颜色状态。

这种方法的缺点是:

  1. 需要修改 PDFJS 源代码。也很大程度上取决于 当前 PDFJS 的实现,如果这是 改变了。

  2. 在将文本用作要填充图像的路径的情况下,它将失败。在某些 PDF 创建器(如 Photoshop)中,它创建彩色文本的方式是,它首先从所有给定的文本字符创建一个剪切路径,然后在该路径上绘制一个纯色图像。因此,推断填充颜色的唯一方法是从图像中读取像素值,这需要将其绘制到 Canvas 上。即使连接到 paintChar 在这里也没有太大帮助,因为填充颜色只会在稍后出现。

好处是,它相当健壮,并且无论页面背景如何都能正常工作。它也不需要将任何东西渲染到 Canvas 上,因此它可以完全在后台线程中完成。

代码

所有修改都在core/evaluator.js 文件中进行。

首先你必须定义自定义评估器,在EvaluatorPreprocessor definition之后.

var CustomEvaluatorPreprocessor = (function() {
    function CustomEvaluatorPreprocessor(stream, xref, stateManager, resources) {
        EvaluatorPreprocessor.call(this, stream, xref, stateManager);
        this.resources = resources;
        this.xref = xref;

        // set initial color state
        var state = this.stateManager.state;
        state.textRenderingMode = TextRenderingMode.FILL;
        state.fillColorSpace = ColorSpace.singletons.gray;
        state.fillColor = [0,0,0];
    }

    CustomEvaluatorPreprocessor.prototype = Object.create(EvaluatorPreprocessor.prototype);

    CustomEvaluatorPreprocessor.prototype.preprocessCommand = function(fn, args) {
        EvaluatorPreprocessor.prototype.preprocessCommand.call(this, fn, args);
        var state = this.stateManager.state;
        switch(fn) {
            case OPS.setFillColorSpace:
                state.fillColorSpace = ColorSpace.parse(args[0], this.xref, this.resources);
            break;
            case OPS.setFillColor:
                 var cs = state.fillColorSpace;
                 state.fillColor = cs.getRgb(args, 0);
            break;
            case OPS.setFillGray:
              state.fillColorSpace = ColorSpace.singletons.gray;
              state.fillColor = ColorSpace.singletons.gray.getRgb(args, 0);
            break;
            case OPS.setFillCMYKColor:
              state.fillColorSpace = ColorSpace.singletons.cmyk;
              state.fillColor = ColorSpace.singletons.cmyk.getRgb(args, 0);
            break;
            case OPS.setFillRGBColor:
                state.fillColorSpace = ColorSpace.singletons.rgb;
                state.fillColor = ColorSpace.singletons.rgb.getRgb(args, 0);
            break;
        }
    };

    return CustomEvaluatorPreprocessor;
})();

接下来需要修改getTextContent method使用新的评估器:

var preprocessor = new CustomEvaluatorPreprocessor(stream, xref, stateManager, resources);

最后,在 newTextChunk方法,添加颜色属性:

color: stateManager.state.fillColor

方法二

另一种方法是通过getTextContent 提取文本边界框,呈现页面,对于每个文本,获取位于其边界内的像素值,并将其作为填充颜色.

这种方法的缺点是:

  1. 计算出的文本边界框并不总是正确的,在某些情况下甚至可能完全不正确(例如:旋转的文本)。如果边界框未至少部分覆盖 Canvas 上的实际文本,则此方法将失败。我们可以从完全失败中恢复,方法是检查文本像素的颜色方差是否大于阈值。理由是,如果边界框完全是背景,它几乎没有变化,在这种情况下我们可以回退到默认文本颜色(或者甚至可能是 k 最近邻的颜色)。
  2. 该方法假定文本比背景暗。否则,背景可能会被误认为填充颜色。在大多数情况下这不会成为问题,因为大多数文档都有白色背景。

好处是,它很简单,不需要弄乱 PDFJS 源代码。此外,它也适用于文本用作剪切路径并填充图像的情况。虽然当您有复杂的图像填充时这可能会变得模糊,在这种情况下,文本颜色的选择会变得模糊。

演示

http://jsfiddle.net/x2rajt5g/

要测试的 PDF 示例:

代码

function parseColors(canvasImgData, texts) {
    var data = canvasImgData.data,
        width = canvasImgData.width,
        height = canvasImgData.height,
        defaultColor = [0, 0, 0],
        minVariance = 20;

    texts.forEach(function (t) {
        var left = Math.floor(t.transform[4]),
            w = Math.round(t.width),
            h = Math.round(t.height),
            bottom = Math.round(height - t.transform[5]),
            top = bottom - h,
            start = (left + (top * width)) * 4,
            color = [],
            best = Infinity,
            stat = new ImageStats();

        for (var i, v, row = 0; row < h; row++) {
            i = start + (row * width * 4);
            for (var col = 0; col < w; col++) {
                if ((v = data[i] + data[i + 1] + data[i + 2]) < best) { // the darker the "better"
                    best = v;
                    color[0] = data[i];
                    color[1] = data[i + 1];
                    color[2] = data[i + 2];
                }
                stat.addPixel(data[i], data[i+1], data[i+2]);
                i += 4;
            }
        }
        var stdDev = stat.getStdDev();
        t.color = stdDev < minVariance ? defaultColor : color;
    });
}

function ImageStats() {
    this.pixelCount = 0;
    this.pixels = [];
    this.rgb = [];
    this.mean = 0;
    this.stdDev = 0;
}

ImageStats.prototype = {
    addPixel: function (r, g, b) {
        if (!this.rgb.length) {
            this.rgb[0] = r;
            this.rgb[1] = g;
            this.rgb[2] = b;
        } else {
            this.rgb[0] += r;
            this.rgb[1] += g;
            this.rgb[2] += b;
        }
        this.pixelCount++;
        this.pixels.push([r,g,b]);
    },

    getStdDev: function() {
        var mean = [
            this.rgb[0] / this.pixelCount,
            this.rgb[1] / this.pixelCount,
            this.rgb[2] / this.pixelCount
        ];
        var diff = [0,0,0];
        this.pixels.forEach(function(p) {
            diff[0] += Math.pow(mean[0] - p[0], 2);
            diff[1] += Math.pow(mean[1] - p[1], 2);
            diff[2] += Math.pow(mean[2] - p[2], 2);
        });
        diff[0] = Math.sqrt(diff[0] / this.pixelCount);
        diff[1] = Math.sqrt(diff[1] / this.pixelCount);
        diff[2] = Math.sqrt(diff[2] / this.pixelCount);
        return diff[0] + diff[1] + diff[2];
    }
};

关于javascript - pdf.js:获取文本颜色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27713660/

相关文章:

javascript - 从 Ajax 生成的 HTML 表到 Highcharts 绘制多个系列

javascript - 谷歌地图根据标记设置不同的 z-index

javascript - 如何为 html5 canvas 制作 jQuery 函数

javascript - PDF Js 在 meteor 中不起作用

asp.net - 强制客户端忽略 PDF.js 的服务器端解决方法(Firefox 的内置查看器以 .aspx 扩展名保存)

internet-explorer - ng2-pdfjs-viewer 检索 PDF 时 Internet Explorer 11 出现意外服务器响应 (500)

javascript - 如何向 PDF.js 查看器添加 UI 和工具栏?

javascript - 在 PHP 中调用 Javascript 函数

javascript - Angular 2 - 查看单个数据记录

javascript - 如何知道 PDF 文件在 PDFjs 上的加载百分比