Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Options having font properties where not expected (image({ font: ... }), textOptions.textBox.style.font = ...?) #182

Open
WolfieZero opened this issue May 14, 2020 · 1 comment
Labels

Comments

@WolfieZero
Copy link

WolfieZero commented May 14, 2020

I've been working on modifying and exporting PDFs with an Electron app; the app will add additional text and images to documents with thanks to this library. But I notices that when I ran certain methods it would crash, only on the built/executable version.

The issue was related to not being able to get the font files (sorry, can't remember the exact error it gave). Looking at previous issue #33, I assumed this was related to not being able to get access to the app's electron.asar file. Placing the font files into the app's respective AppData folder, I was still getting the issue. I went back to try a hack with HummusJs directly to add text to a document and that worked fine with my font file in the AppData directory so conclusion was it's hummusRecipe.

I modified the lines here to do some back-tracing and seeing what options was being passed and saw it was showing options from image() but also textBox.style options.

After adding a font property to those options it worked correctly.

It would appear that hummusRecipe is trying to grab a font file for properties where it needn't, nor does the documentation show that I would ever need to add the font property to those options.

I've not had a chance to dive much deeper to see why they are getting called other than maybe I'm registering fonts immediately after creating an instance of HummusRecipe()

this.pdfDoc = new HummusRecipe(input, output, {
	fontSrcPath: this.font.directory,
})
this.pdfDoc.registerFont(this.font.name, path.join(this.font.directory, this.font.file))

If I get a chance to go deeper I'll let you know.

Thanks :)

@shaehn
Copy link
Collaborator

shaehn commented Jul 23, 2020

@chunyenHuang I think this is a problem with the overloading of the _getPathOptions function. It has become a catch-all for object initialization. Mea-culpa. How about I split up the function into 2 separate functions, _getTextOptions and _getPathOptions. I will remove all the textual attributes from getPathOptions, place them in _getTextOptions and have it call _getPathOptions to fill in the remainder. (Text options are a superset of normal path options). Then where the text function is calling _getPathOptions, have it call _getTextOptions instead. Also, replace the call to _getPathOptions found in the getCellHeight function of file table.js with the _getTextOptions call.

See the following:

In text.js

exports._getTextOptions = function _getTextOptions(options = {}, originX, originY) {
    this.current = this.current || {};

    const textOptions = {
        font: this._getFont(options),
        size: options.size || this.current.defaultFontSize,
        charSpace: options.charSpace || 0,
        underline: false,
        align: options.align
    };

    if (options.size || options.fontSize) {
        const size = options.size || options.fontSize;
        if (!isNaN(size)) {
            textOptions.size = (size <= 0) ? 1 : size;
        }
    }
    
    return Object.assign(textOptions, this._getPathOptions(options, originX, originY));
};

In vector_helper.js

exports._getPathOptions = function _getPathOptions(options = {}, originX, originY) {
    const colorspace = options.colorspace || this.options.colorspace;
    const colorName = options.colorName;

    const pathOptions = {
        originX,
        originY,
        color: this._transformColor(options.color, {colorspace:colorspace, colorName:options.colorName}),
        colorspace,
        colorName,
        colorArray: [],
        lineCap: this._lineCap(),
        lineJoin: this._lineJoin(),
        miterLimit: 1.414,
        width: 2
    };

    if (options.opacity == void(0) || isNaN(options.opacity)) {
        options.opacity = 1;
    } else {
        options.opacity = (options.opacity < 0) ? 0 :
            (options.opacity > 1) ? 1 : options.opacity;
    }
    pathOptions.opacity = options.opacity;
    const extGStates = this._createExtGStates(options.opacity);
    pathOptions.strokeGsId = extGStates.stroke;
    pathOptions.fillGsId = extGStates.fill;

    if (options.width || options.lineWidth) {
        const width = options.width || options.lineWidth;
        if (!isNaN(width)) {
            pathOptions.width = (width <= 0) ? 1 : width;
        }
    }

    const colorOpts = {colorspace:colorspace, wantColorModel:true, colorName: options.colorName};

    if (options.stroke) {
        pathOptions.strokeModel = this._transformColor(options.stroke, colorOpts);
        pathOptions.stroke = pathOptions.strokeModel.color;
    }

    if (options.fill) {
        pathOptions.fillModel = this._transformColor(options.fill, colorOpts);
        pathOptions.fill = pathOptions.fillModel.color;
    }

    pathOptions.colorModel = this._transformColor((options.color || options.colour), colorOpts);
    pathOptions.color = pathOptions.colorModel.color;
    pathOptions.colorspace = pathOptions.colorModel.colorspace;

    // rotation
    if (options.rotation !== void(0)) {
        const rotation = parseFloat(options.rotation);
        pathOptions.rotation = rotation;
        pathOptions.rotationOrigin = options.rotationOrigin || null;
    }

    // skew
    if (options.skewX !== void(0)) {
        pathOptions.skewX = options.skewX;
    }

    if (options.skewY != void(0)) {
        pathOptions.skewY = options.skewY;
    }

    // Page 127 of PDF 1.7 specification
    pathOptions.dash = (Array.isArray(options.dash)) ? options.dash : [];
    pathOptions.dashPhase = (!isNaN(options.dashPhase)) ? options.dashPhase : 0;
    if (pathOptions.dash[0] == 0 && pathOptions.dash[1] == 0) {
        pathOptions.dash = [];     // no dash, solid unbroken line
        pathOptions.dashPhase = 0;
    }

    // Page 125-126 of PDF 1.7 specification
    if (options.lineJoin !== void(0)) {
        pathOptions.lineJoin = this._lineJoin(options.lineJoin);
    }
    if (options.lineCap !== void(0)) {
        pathOptions.lineCap = this._lineCap(options.lineCap);
    }

    if (options.miterLimit !== void(0)) {
        if (!isNaN(options.miterLimit)) {
            pathOptions.miterLimit = options.miterLimit;
        }
    }

    return pathOptions;
};

I have made such changes locally and the library tests seem to pass ok (no errors popped in output), but I cannot be sure if it cures @WolfieZero problem. He would have to actually due the proof of concept test to verify.

@chunyenHuang chunyenHuang pinned this issue Jul 24, 2020
@chunyenHuang chunyenHuang unpinned this issue Oct 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants