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

Skia rect dashed path effect for svg #177

Closed
peterchenadded opened this issue May 18, 2022 · 20 comments
Closed

Skia rect dashed path effect for svg #177

peterchenadded opened this issue May 18, 2022 · 20 comments

Comments

@peterchenadded
Copy link

peterchenadded commented May 18, 2022

It seems the SVG canvas does not apply any patheffects for canvas.drawRect(...)

from IPython.core.display import SVG
import io

stream = skia.DynamicMemoryWStream()
canvas = skia.SVGCanvas.Make((100, 100), stream)

rect = skia.Rect(10, 10, 50, 50)
paint = skia.Paint(
            AntiAlias=True,
            PathEffect=skia.DashPathEffect.Make([10.0, 5.0], 0.0),
            Style=skia.Paint.kStroke_Style,
            StrokeWidth=1,
)
canvas.drawRect(rect, paint)

del canvas
stream.flush()

SVG(stream.detachAsData())

image

The patheffects e.g. dashedpatheffect is being applied for PNG images without issues.

surface = skia.Surface(100, 100)
canvas = surface.getCanvas()
rect = skia.Rect(10, 10, 50, 50)
paint = skia.Paint(
        AntiAlias=True,
        PathEffect=skia.DashPathEffect.Make([10.0, 5.0], 0.0),
        Style=skia.Paint.kStroke_Style,
        StrokeWidth=1,
)
canvas.drawRect(rect, paint)
image = surface.makeImageSnapshot()

display(image)

image

I know we can use path.addRect(...), but the output xml is very large.

Does anyone know if it possible to add a stroke-dasharray attribute directly to a rect in the output .svg instead?

Cheers,

@peterchenadded peterchenadded changed the title Skia rect dashed path rect for svg Skia rect dashed path effect for svg May 18, 2022
@HinTak
Copy link
Collaborator

HinTak commented Aug 5, 2023

When I tried this code above with m116, I have a mesageUnsupported path effect in addPaint. on my console. It is not from skia-python, so it seems it is from skia itself. Do you not see this message with m87?

@HinTak
Copy link
Collaborator

HinTak commented Aug 9, 2023

The "Unsupported path effect in addPaint." message apparently is also in m87. It is emitted aftercanvas.drawRect(rect, paint) , and from skia itself.

@peterchenadded
Copy link
Author

Oh ok. Closing this. We had to work around it by doing search and replace after the fact.

@peterchenadded
Copy link
Author

Thanks for investigating this for me!

@HinTak
Copy link
Collaborator

HinTak commented Jul 18, 2024

@peterchenadded strangely enough, I retry with the upcoming m127 (#251), and the message is no longer displayed. Although it is still in the code https://github.com/google/skia/blob/9d391088f52c039f1988b6ef469d62442b6d8690/src/svg/SkSVGDevice.cpp#L363 . Looking at the code, it is supposed NOT to be reached if they support that later, and indeed they seems to have added it a few months ago: google/skia@5f253bf

It looks like this was added in m124. So it looks like if you use skia-python 124, 126, or the upcoming 127, it works.

@HinTak
Copy link
Collaborator

HinTak commented Jul 18, 2024

It looks as if somebody else raised your exact issue in 4 months ago in March on the skia-discuss group and moved forward and submitted a patch to make this work!

@peterchenadded
Copy link
Author

Thanks @HinTak, skia-python 124, 126 is still beta right?

Also, was looking at v120 release notes and noticed, option to use freetype as fontmgr, does this mean the text height and width calculations are actually correct now? I remember having to bring in freetype library ourselves to do the calculations.

@HinTak
Copy link
Collaborator

HinTak commented Jul 18, 2024

@peterchenadded yes, it will be beta to about 13x, probably close to 140. Mainly some corners which m87 does still doesn't work. (Some of it is really that m87 exposed too much of the shifting target of upstream skia. But once it is exposed, some users might be upset if it disappears, so we are just gradually looking at the m87 to m1xx differences and try to have some emulation or at least a tips on what the m1xx equivalents of m87 functionality is)

As for width height calculations,

    text = "abcdefgh"
    font = skia.Font("Times Roman",40)
    blob = skia.TextBlob.MakeFromText(text, font)
    bounds = blob.bounds()

Should work. Did it not? (With whatever fontmgr). The above snippet adapted from the current pytest code. If you need non-latin / RTL language support, use MakeFromShapedText instead in the middle. That should deal with any tricky kerning in unusual fonts with unusual spacings too.

@peterchenadded
Copy link
Author

peterchenadded commented Jul 18, 2024

PXL_20240718_034539757

It's more complicated...

  1. The first line is using bounds and gets something bigger and is actually noted in the bounds docstring
  2. The second line is from using freetype and adding character by character
  3. The third line is trying to remove the white space at the start and end. Bounds.fLeft actually gives a negative number relative to the start point. So here width is Bounds.fRight + Bounds.fLeft
  4. The forth line is using font.measureText(font, text) but I know this isn't cross platform and gives different values on Mac, Linux and Windows

Do you happen to have the code to change the fontmgr? I was never able to work that out last year when I was trying.

@HinTak
Copy link
Collaborator

HinTak commented Jul 18, 2024

It is a class static method: e.g. skia.FontMgr.New_Custom_Empty().makeFromFile(svgfont_path). (From my fork's , which have some more font tests).

I'll need a concrete example to look at the width/height issue.

@peterchenadded
Copy link
Author

Isn't that something else? It's loading a font file rather than selecting the type of font render such as freetype.

I can provide a reproducer example of the width/height issue without the freetype line when I get access to my laptop.

@HinTak
Copy link
Collaborator

HinTak commented Jul 18, 2024

That's what it is - it uses freetype on every platform to load a font file. E.g. on windows you pass in c:/windows/fonts/times.ttf, and on mac from /Library/Fonts/, or one on the current directory. You use it with a font file name.

@HinTak
Copy link
Collaborator

HinTak commented Jul 18, 2024

If you use the by fontname routine, it is quite tricky to try to get to exactly the system font you want. E.g. if you ask for a emoji from the named 'Times Roman" font, depending on OS, and personal settings, the system can silently substitute another font for you, or do something else (fails, or returns the .nodef glyph - often either a space or a crossed box - ).

@HinTak
Copy link
Collaborator

HinTak commented Jul 18, 2024

Skia in chrome does something a bit more clever, it can occasionally open a font file (from system, from local custom location, or as Web fonts from the internet), then realises mac/windows can't handle that particular type of font file, and passes it to freetype. I think this is quite possible with skia-python too - you get the memorystream out of the system's fontmgr, and call skia.FontMgr.New_Custom_Empty().readfromStream(...) or something along that line.

@peterchenadded
Copy link
Author

That's what it is - it uses freetype on every platform to load a font file.

Did that just recently start happening?

I thought it would use the native OS specific render? I know last year I used skia.Typeface.MakeFromFile to load the font file and use that to output the text but across different OS the output dimensions would be inconsistent.

I was not aware of that static method maybe that one does use freetype. Will give it a try and see what bounds it returns.

@HinTak
Copy link
Collaborator

HinTak commented Jul 18, 2024

skia.FontMgr.New_Custom_Empty().makeFromFile(svgfont_path) is recently added. skia.FontMgr().makeFromFile(svgfont_path) (the default constructor) is equivalent to skia.Typeface.MakeFromFile(), using the system render.

The custom empty name is confusing, but custom just means non-OS-vendor (I.e. freetype on non-Linux), and Empty means no-preloaded-system-fonts.

@HinTak
Copy link
Collaborator

HinTak commented Jul 20, 2024

@peterchenadded your comment about the spacing seems vaguely similar to #168 - any relations? I have made a new comment on it. I don't know what @kyamagu 's position on this is, I am not being paid to work on skia-python, so there is a max amount of time I want to spend on it in any period of time that way. If you use it for any production use, and like to move the m1xx out of beta sooner, or commission specific changes/additions, or have dev work based on skia-python you want to fund, please feel free to use the donate link under my profile, or
get in touch privately. (My email in the commit message is reachable)

FWIW, even experts gave up on this - different kind of bounds for different purposes (harfbuzz/harfbuzz#79 was closed without addressing it - the owner just got annoyed somebody else wrote a python parallel implementation without contributing a c++ pull).

@peterchenadded
Copy link
Author

peterchenadded commented Jul 20, 2024

@HinTak thanks for the awesome responses.

your comment about the spacing seems vaguely similar to #168 - any relations

Yes it is some what related. There were really three separate problems.

  1. Getting the bounds of the text correctly.
  2. Getting the bounds of the text correctly across all OS.
  3. Consistent bounds across all OS - the same value!

I needed all three to be resolved.

Anyway last year I resolved it by using freetype and not doing any bounds/measureText in python-skia.

If you use it for any production use, and like to move the m1xx out of beta sooner, or commission specific changes/additions, or have dev work based on skia-python you want to fund, please feel free to use the donate link under my profile, or
get in touch privately. (My email in the commit message is reachable)

Again thanks for your help, I will keep that in mind if we do decide to extend out our usage of python-skia.

FWIW, even experts gave up on this - different kind of bounds for different purposes (harfbuzz/harfbuzz#79 was closed without addressing it - the owner just got annoyed somebody else wrote a python parallel implementation without contributing a c++ pull).

Yeah I tend to agree. We were outputting to svg across multiple OS and we had tests as well. So the bound values had to be exactly the same across all OS. However, each OS has its own font render so when rendered may still be off when viewed. But for us the tests were more important. The difference from user perspective was minimal or even noticable.

@kyamagu
Copy link
Owner

kyamagu commented Jul 20, 2024

@HinTak Thanks for investigating this problem. I am not an expert on this topic and have no clue how to fix it. However, given that even web browsers have different rendering results across platforms, I suspect this needs at least forcing the same backend (freetype here) and testing the behavior.

@HinTak
Copy link
Collaborator

HinTak commented Jul 20, 2024

If you want more or less the same result across different OSes, you'd have to use one of the newer beta with New_Custom_Empty() and load your own fonts: skia.FontMgr.New_Custom_Empty().makeFromFile(font_path) . I wrote "more or less" as there could be small rounding differences between OSes if some numbers falls too close to 0.5 .

You mean you want the ink box? Most of the "bounds" calculation in skia is geared towards text layout (I.e. comfortable reading) I would guess, having normal line and word spacings (wouldn't surprise me their TextBlob bounds have built-in horizontal paddings to make sure "blobs" next to each others are visually distinct), so quite unlikely to be the ink box, the smallest rect box enclosing all the ink. Skia/chrome is not trying to treat text as graphics elements like illustrator or something.

I reckon if you use the new New_Custom_Empty , measureText would probably behaves the way you want across platforms. (Up to the rounding/maths issue)

(I am also one of the collaborators of freetype-py; and on the freetype-devel mailing list for about 2 decades, I 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

3 participants