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

Character rendering issues occur when stroking text. #707

Open
ILOVEPIE opened this issue Apr 30, 2024 · 11 comments
Open

Character rendering issues occur when stroking text. #707

ILOVEPIE opened this issue Apr 30, 2024 · 11 comments

Comments

@ILOVEPIE
Copy link
Contributor

Character rendering issues occur when stroking text.

Expected Behavior

The stroke should match the fill.

Current Behavior

Random dots of stroke appear sometimes, also some lines cut inwards into the fill.
cutting_in
cutting_in_2
dots

Possible Solution

Unknown

Steps to Reproduce (for bugs)

I'll provide a link to the other maintainers so we can narrow down a test-case.

Context

I need to provide psychovisually lossless rendering of Advanced SubStation Alpha, this bug is causing issues for me.

Your Environment

@axkibe
Copy link
Contributor

axkibe commented May 9, 2024

Work for me.

index.html:

<html>
	<script src="opentype.js" defer></script>
	<script src="script.js" defer></script>
<body>
<canvas id="canvas"></canvas>
</body>
</html>

script.js:

window.onload =
	async function( )
{
	const buffer = await ( await fetch('franklin.ttf') ).arrayBuffer( );
	const font = opentype.parse( buffer );

	const canvas = document.getElementById( 'canvas' );
	canvas.width = 1000;
	canvas.height = 400;
	const cx = canvas.getContext( '2d' );

	const path = font.getPath( 'Japan! Tokyo', 10, 80, 100, { } );
	path.stroke = 'blue';
	path.strokeWidth = 3;
	path.fill = 'lightgray';
	cx.lineJoin = 'round';
	cx.lineCap = 'round';
	path.draw( cx );
}

franklin.ttf: from here:
https://fontsgeek.com/fonts/Franklin-Gothic-Book-Regular

result:
Screenshot from 2024-05-09 07-46-18

PS: What I do noticed, is that some glyph would benefit from calling closePath() what I believe is not done.

Otherwise can you modify my minimal example so it makes the error you are seeing?

PPS: for completness, just put the files in a directory for example called web and
npm install http-server
./node_modules/.bin/http-server -c-1 web/
to run it.

@ILOVEPIE
Copy link
Contributor Author

ILOVEPIE commented May 9, 2024

This issue was mostly created so that I could keep track of my progress looking into the issue, but if you're willing to help me track this down, thanks. The way I'm doing this is iterating through the glyphs and drawing them individually instead of the whole thing all at once (because I have to composite them with webgl). The version of the font I'm using is identical to the one you found. Here's a simplified version of the drawGlyph function I'm using:

function drawGlyph (glyph, offsetX, offsetY, stroke) {
    const fontSize = this._fontInfo.size;
    const fontUnitsScale = this._fontInfo.font.unitsPerEm || 1000;
    const fontSizeMultiplier = fontSize / fontUnitsScale;
    const yoffset =
        this._fontInfo.font.ascender * fontSizeMultiplier;
    const path = glyph.getPath(
        offsetX,
        offsetY + yoffset,
        fontSize
    );
    path.fill = null;
    path.stroke = null;
    path.draw(this._ctx);
    if (stroke) {
        this._ctx.stroke();
    } else {
        this._ctx.fill();
    }
}

@ILOVEPIE
Copy link
Contributor Author

ILOVEPIE commented May 10, 2024

I'm going to have to adapt it for variable fonts, but that comes after resolving this bug.
adding closePath doesn't seem to fix it

@Connum
Copy link
Contributor

Connum commented May 14, 2024

What I do noticed, is that some glyph would benefit from calling closePath() what I believe is not done.

Do you have examples for this? While implementing variable fonts, I added a performance optimisation so that closePath() is only called when a glyph has a stroke applied, because the command is automatically executed for filled fonts, which resulted in unnecessary overhead because it was then called twice.

@axkibe
Copy link
Contributor

axkibe commented May 15, 2024

What I do noticed, is that some glyph would benefit from calling closePath() what I believe is not done.

Do you have examples for this? While implementing variable fonts, I added a performance optimisation so that closePath() is only called when a glyph has a stroke applied, because the command is automatically executed for filled fonts, which resulted in unnecessary overhead because it was then called twice.

Well I guess there you have the issue, ILOVEPIE introduced it by setting stroke to null, then stroking himself. So paths are not closed.

BTW: I really dislike the API design in the first place. The API should simply be path.sketch() or follow() or whatever verb matches, and the caller should then call stroke and or fill. As you seen in my example the lineJoin/lineCap things are missing for example anyway.

When I was testing this with lineCaps as butts the ! for example wasn't closed.

@ILOVEPIE
Copy link
Contributor Author

Yeah, There's some issues with the drawing code. I'll rework it some.

@axkibe
Copy link
Contributor

axkibe commented May 28, 2024

BTW: If all you need it filling and stroking of text.. do you need opentype at all? canvas.fillText and canvas.strokeText should be fine for this. (I needed opentype.js for rendering, because I needed endless zooming where these functions had a size limit and they are missing getAdvanceWith() for caret based editors, but otherwise for this example I see it as unnessary complication to use this library at all..)

@ILOVEPIE
Copy link
Contributor Author

ILOVEPIE commented Jun 2, 2024

BTW: If all you need it filling and stroking of text.. do you need opentype at all? canvas.fillText and canvas.strokeText should be fine for this. (I needed opentype.js for rendering, because I needed endless zooming where these functions had a size limit and they are missing getAdvanceWith() for caret based editors, but otherwise for this example I see it as unnessary complication to use this library at all..)

I have to use OpenType.js because I have to have access to the information required to render it like Windows does because I have to emulate Windows font rendering for my project.

@axkibe
Copy link
Contributor

axkibe commented Jun 5, 2024

OK, how is it different than canvas with converted to .woff does?

@talltyler
Copy link

From my understanding this line https://github.com/opentypejs/opentype.js/blob/master/src/glyph.mjs#L235
should be something like this } else if (cmd.type === 'Z' && ((p.stroke && p.strokeWidth) || options.closePaths)) {
to enable drawing with completed paths outside of the internal drawing code, this maybe could also be done if options.drawSVG is true. What do you all think? Should I make a PR?

@utunnels
Copy link

From my understanding this line https://github.com/opentypejs/opentype.js/blob/master/src/glyph.mjs#L235 should be something like this } else if (cmd.type === 'Z' && ((p.stroke && p.strokeWidth) || options.closePaths)) { to enable drawing with completed paths outside of the internal drawing code, this maybe could also be done if options.drawSVG is true. What do you all think? Should I make a PR?

I think path should also be closed when M command is used. Or at least when a glyph ends. Because I notice Some font just don't use Z command. But maybe that's a mistake of the font. What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants