call_end

    • chevron_right

      Jussi Pakkanen: Unit testing PDF generation

      news.movim.eu / PlanetGnome • 27 February, 2023 • 2 minutes

    How would you test PDF generation?

    This turns out to be unexpectedly difficult because you need to check that the files are both syntactically and semantically valid. The former could be tested with existing PDF validators and converters but the latter is more difficult.

    If you, say, try to render a red square, the end result should be that the PDF command stream has two commands, a re command and an f command. That could be verified simply by grepping the command stream with some regexps. It would not be very useful, though, as there is no guarantee that those commands actually produce a red square in the output document. There are dozens of ways to make the output stream not produce a red square in the intended location without breaking file "validity" in any way.

    What even is the truth?

    The only reliable way is to render the PDF file into an image and compare it to a ground truth image. Assuming the output is "close enough" then the generator can be said to have worked correctly. The problem, as is often the case, lies inside those quote marks. Fuzzy image equality is a difficult problem. Those interested in details can look it up online. For our case we'll just ignore it and require pixel perfect reproduction. This means that we can have test failures if we change the PDF rendering backend, run it on a different operating system or even just upgrade it to a new version.

    The other problem comes from the desire to have a plain C API. Writing unit tests in C is cumbersome to say the least. Fortunately there is a simpler solution. Since the project ships its own Python bindings, we can write all of these tests using Python. This affords us all the niceties that exist in Python such as an extensive unit testing suite, the ability to easily spawn external processes and image difference operators (via PIL). After some boilerplate, writing an unit tests reduces to this:

    @validate_image('python_simple', 480, 640)
    def test_simple(self, ofilename):
    ofile = pathlib.Path(ofilename)
    with a4pdf.Generator(ofile) as g:
    with g.page_draw_context() as ctx:
    ctx.set_rgb_nonstroke(1.0, 0.0, 0.0)
    ctx.cmd_re(10, 10, 100, 100)
    ctx.cmd_f()

    Behind the scenes this will generate the PDF, render it with Ghostscript and compare the result to an existing image. If the output is not bitwise identical the test fails.

    Get the code

    The project is now available here .
    • chevron_right

      Felipe Borges: GSoC 2023: GNOME Foundation has been accepted as a mentoring org!

      news.movim.eu / PlanetGnome • 27 February, 2023 • 2 minutes

    We are glad to announce that once again the GNOME Foundation will be part of Google Summer of Code . We are interested in onboarding new contributors that are passionate about GNOME and motivated to become long term GNOME developers!

    @Contributors interested in participating in GSoC with GNOME should visit https://gsoc.gnome.org for more information.

    @Mentors interested in mentoring projects this year should file a gitlab issue in Teams / Engagement / Internship Project Ideas · GitLab 2

    Project ideas

    Our ideas list is available in GNOME + GSoC | 2023 Project Ideas 2 and is the result of the discussions in Teams / Engagement / Internship Project Ideas · GitLab 2 .

    You can still submit project ideas until March 19th, when GSoC applicants are expected to submit their final proposals.

    Important upcoming dates:

    • Now – March 19: Proactive GSoC contributors will reach out asking questions about your ideas list and receive feedback from us so they can start crafting their project proposals.

      @Contributors make sure you research enough about the project and work towards making a small contribution. You should consider the proposals available in GNOME + GSoC | 2023 Project Ideas or propose your own project ideas as soon as possible in Teams / Engagement / Internship Project Ideas · GitLab 2
      Make sure you approach potential mentors to move your idea towards an internship.

      @Mentors , point contributors to gsoc.gnome.org for more information and be patient with their questions. Contributors are open to suggest new project proposals and you should indicate whether you’d be interested in mentoring those proposals and help them compose a project proposal that is realistic and benefits the project.

    • March 20 – April 4 18:00 UTC : GSoC contributors will submit their proposals through the program website.
    • April 4 – 26: We (admins and mentors) will review all submitted GSoC Contributor proposals and consider how many we want to select (based on how many committed mentors we have available). Admins and mentors will rank the contribution proposals.
    • April 27 18:00 UTC : Deadline to submit ranked slot requests (Org Admins enter requests)
    • April 27 – May 2: Google Program Admins review and assign org slots
    • May 3: Organizations receive notification of their accepted GSoC 2023 Contributors
    • May 4 : Accepted GSoC 2023 GSoC Contributor projects are announced
    • May 4 – 28: Community Bonding Period
    • May 27 : Deadline to notify Google Admins of an inactive GSoC Contributor that you wish to remove from the program
    • May 29 : Coding begins

    For more information on the timeline, visit Google Summer of Code 2023 Timeline  |  Google Developers

    If you have any doubts or questions, please reply to this message on Discourse .

    • wifi_tethering open_in_new

      This post is public

      feborg.es /gsoc-2023-gnome-foundation-has-been-accepted-as-a-mentoring-org/

    • chevron_right

      Alexander Mikhaylenko: Introducing Elastic

      news.movim.eu / PlanetGnome • 24 February, 2023 • 2 minutes

    Elastic is a new spring animation editor app.

    Screenshot of Elastic

    Ever since 1.0, libadwaita has had spring animations . These animations aren’t controlled with a duration and an easing function, but instead with physical properties: damping ratio (or optionally damping), mass, stiffness and initial velocity, as well as epsilon. While this allows for a lot of control over the animation, it can be pretty hard to understand if you’re not familiar with physics involved, and to be truly useful it needs an editor.

    So, Elastic is that editor. It provides a way to tweak each parameter, explains what they do, allows to preview the animation in various ways, and generates the code to create that animation.

    Screenshot of the animation graph in Elastic

    Backstory

    This app has a pretty convoluted history.

    At one point of time, before 1.0, me and Tobias wanted to rework libadwaita demo and Tobias made new mockups for it. At about the same time, I was experimenting to see how feasible spring animations would be and needed some playground. Since it was understood that this will eventually be in libadwaita, Tobias made this design a part of the new demo:

    Spring animation demo mockups

    So I implemented it about as closely as I could at the time, but since then a few things happened:

    • We decided that the new demo was too design-focused to work well as, well, a demo (one of the purposes of which is using it as a test case for libadwaita itself). So, instead, we decided to make the new design a separate app called Patterns . That app is stalled for other reasons as it needs features that libadwaita doesn’t provide at the moment. Hopefully, next cycle we should have everything needed to finish it.
    • We decided that the spring animation page was too involved for a demo and was better off separately. So I split that code out and ported it to Vala instead of C.
    • As part of his GSoC project, Manuel Genovés implemented proper spring animations instead of a hacky prototype, and I ported the editor to that.

    And then… everyone kinda forgot about it – Patterns was still blocked on other things, Manuel implemented a simpler animation demo for libadwaita and this one was left out.

    Fast forward two years, Tobias was reviewing an app in Circle and needed a way to play with spring animations. We realized we never did anything with the editor, so I turned it into a proper app, fixed the remaining bugs and made it generate code instead of just previewing animations. We named it Spring Editor (imaginative, I know) and my impression was that design tools were covered by the new app inclusion process, so I submitted it to incubator.

    It turned out more complicated than expected, so I decided to ignore it and try and get it into GNOME Circle instead. So now the Spring Editor name is too generic, so the app got renamed again, this time to Elastic (thanks Christopher Davis for that name) and it’s on Flathub now.


    Anyway, the app is finally out, it’s on Flathub, so have fun. :)

    Download on Flathub

    • wifi_tethering open_in_new

      This post is public

      blogs.gnome.org /alexm/2023/02/24/introducing-elastic/

    • chevron_right

      Philip Withnall: Tip for debugging refcounting issues: change ref calls to copy

      news.movim.eu / PlanetGnome • 23 February, 2023 • 1 minute

    Over the last couple of days I’ve been looking at a refcounting issue in GLib’s D-Bus implementation .

    As with many things in GLib, the allocations in this code use refcounting, rather than new/free, to manage object lifecycles. This makes it quite hard to debug lifecycle issues, because the location of a bug (a ref or unref call which isn’t correct) can be quite far removed (in time and code) from where the effects of that bug become visible. This is because the effects of refcounting problems only become visible when an object’s refcount reaches zero, or when the program ends and its refcount still hasn’t reached zero.

    While debugging this code, I tried an approach I haven’t before: changing some of the ref calls on the buggy object to be copy calls instead . (Specifically, changing g_object_ref() to g_dbus_message_copy() .) That split up the lifecycle of the object into smaller pieces , narrowing down the sets of ref/unref calls which could be buggy. Ultimately, this allowed me to find some bugs in the code, and hopefully those are the bugs causing the refcounting issue. Since the issue is intermittent, it’s a bit hard to be sure.

    This debugging approach was possible in this case because the object I was debugging is immutable, so passing around copies of it doesn’t affect the behaviour of other bits of code vs passing around the original. Hence this approach is only applicable in some situations. But it’s another good reason why using immutable objects is quite helpful when writing code, and it’s certainly an approach I’m going to be using again when I can.

    • wifi_tethering open_in_new

      This post is public

      tecnocode.co.uk /2023/02/23/tip-for-debugging-refcounting-issues-change-ref-calls-to-copy/

    • chevron_right

      Emmanuele Bassi: Writing Bindable API, 2023 Edition

      news.movim.eu / PlanetGnome • 20 February, 2023 • 12 minutes

    First of all, you should go on the gobject-introspection website and read the page on how to write bindable API . What I’m going to write here is going to build upon what’s already documented, or will update the best practices, so if you maintain a GObject/C library, or you’re writing one, you must be familiar with the basics of gobject-introspection. It’s 2023: it’s already too bad we’re still writing C libraries, we should at the very least be responsible about it.

    A specific note for people maintaining an existing GObject/C library with an API designed before the mainstream establishment of gobject-introspection (basically, anything written prior to 2011): you should really consider writing all new types and entry points with gobject-introspection in mind, and you should also consider phasing out older API and replacing it piecemeal with a bindable one. You should have done this 10 years ago, and I can already hear the objections, but: too bad . Just because you made an effort 10 years ago it doesn’t mean things are frozen in time, and you don’t get to fix things. Maintenance means constantly tending to your code, and that doubly applies if you’re exposing an API to other people.


    Let’s take the “how to write bindable API ” recommendations, and elaborate them a bit.

    Structures with custom memory management

    The recommendation is to use GBoxed as a way to specify a copy and a free function, in order to clearly define the memory management semantics of a type.

    The important caveat is that boxed types are necessary for:

    • opaque types that can only be heap allocated
    • using a type as a GObject property
    • using a type as an argument or return value for a GObject signal

    You don’t need a boxed type for the following cases:

    • your type is an argument or return value for a method, function, or virtual function
    • your type can be placed on the stack, or can be allocated with malloc() / free()

    Additionally, starting with gobject-introspection 1.76, you can specify the copy and free function of a type without necessarily registering a boxed type, which leaves boxed types for the thing they were created: signals and properties.

    Addendum: object types

    Boxed types should only ever be used for plain old data types; if you need inheritance, then the strong recommendation is to use GObject . You can use GTypeInstance , but only if you know what you’re doing ; for more information on that, see my old blog post about typed instances .

    Functionality only accessible through a C macro

    This ought to be fairly uncontroversial. C pre-processor symbols don’t exist at the ABI level, and gobject-introspection is a mechanism to describe a C ABI . Never, ever expose API only through C macros; those are for C developers. C macros can be used to create convenience wrappers, but remember that anything they call must be public API , and that other people will need to re-implement the convenience wrappers themselves, so don’t overdo it. C developers deserve some convenience, but not at the expense of everyone else.

    Addendum: inline functions

    Static inline functions are also not part of the ABI of a library, because they cannot be used with dlsym() ; you can provide inlined functions for performance reasons, but remember to always provide their non-inlined equivalent.

    Direct C structure access for objects

    Again, another fairly uncontroversial rule. You shouldn’t be putting anything into an instance structure, as it makes your API harder to future-proof, and direct access cannot do things like change notification, or memoization .

    Always provide accessor functions.

    va_list

    Variadic argument functions are mainly C convenience. Yes, some languages can support them, but it’s a bad idea to have this kind of API exposed as the only way to do things.

    Any variadic argument function should have two additional variants:

    • a vector based version, using C arrays (zero terminated, or with an explicit length)
    • a va_list version, to be used when creating wrappers with variadic arguments themselves

    The va_list variant is kind of optional, since not many people go around writing variadic argument C wrappers, these days, but at the end of the day you might be going to write an internal function that takes a va_list anyway, so it’s not particularly strange to expose it as part of your public API .

    The vector-based variant, on the other hand, is fundamental.

    Incidentally, if you’re using variadic arguments as a way to collect similarly typed values, e.g.:

    // void
    // some_object_method (SomeObject *self,
    //                     ...) G_GNUC_NULL_TERMINATED
    
    some_object_method (obj, "foo", "bar", "baz", NULL);
    

    there’s very little difference to using a vector and C99’s compound literals:

    // void
    // some_object_method (SomeObject *self,
    //                     const char *args[])
    
    some_object_method (obj, (const char *[]) {
                          "foo",
                          "bar",
                          "baz",
                          NULL,
                        });
    

    Except that now the compiler will be able to do some basic type check and scream at you if you’re doing something egregiously bad.

    Compound literals and designated initialisers also help when dealing with key/value pairs:

    typedef struct {
      int column;
      union {
        const char *v_str;
        int v_int;
      } value;
    } ColumnValue;
    
    enum {
      COLUMN_NAME,
      COLUMN_AGE,
      N_COLUMNS
    };
    
    // void
    // some_object_method (SomeObject *self,
    //                     size_t n_columns,
    //                     const ColumnValue values[])
    
    some_object_method (obj, 2,
      (ColumnValue []) {
        { .column = COLUMN_NAME, .data = { .v_str = "Emmanuele" } },
        { .column = COLUMN_AGE, .data = { .v_int = 42 } },
      });
    

    So you should seriously reconsider the amount of variadic arguments convenience functions you expose.

    Multiple out parameters

    Using a structured type with a out direction is a good recommendation as a way to both limit the amount of out arguments and provide some future-proofing for your API . It’s easy to expand an opaque pointer type with accessors, whereas adding more out arguments requires an ABI break.

    Addendum: inout arguments

    Don’t use in-out arguments. Just don’t.

    Pass an in argument to the callable for its input, and take an out argument or a return value for the output.

    Memory management and ownership of inout arguments is incredibly hard to capture with static annotations; it mainly works for scalar values, so:

    void
    some_object_update_matrix (SomeObject *self,
                               double *xx,
                               double *yy,
                               double *xy,
                               double *yx)
    

    can work with xx , yy , xy , yx as inout arguments, because there’s no ownership transfer; but as soon as you start throwing things in like pointers to structures, or vectors of string, you open yourself to questions like:

    • who allocates the argument when it goes in?
    • who is responsible for freeing the argument when it comes out?
    • what happens if the function frees the argument in the in direction and then re-allocates the out ?
    • what happens if the function uses a different allocator than the one used by the caller?
    • what happens if the function has to allocate more memory?
    • what happens if the function modifies the argument and frees memory?

    Even if gobject-introspection nailed down the rules, they could not be enforced, or validated, and could lead to leaks or, worse, crashes.

    So, once again: don’t use inout arguments. If your API already exposes inout arguments, especially for non-scalar types, consider deprecations and adding new entry points.

    Addendum: GValue

    Sadly, GValue is one of the most notable cases of inout abuse. The oldest parts of the GNOME stack use GValue in a way that requires inout annotations because they expect the caller to:

    • initialise a GValue with the desired type
    • pass the address of the value
    • let the function fill the value

    The caller is then left with calling g_value_unset() in order to free the resources associated with a GValue . This means that you’re passing an initialised value to a callable, the callable will do something to it (which may or may not even entail re-allocating the value) and then you’re going to get it back at the same address.

    It would be a lot easier if the API left the job of initialising the GValue to the callee; then functions could annotate the GValue argument with out and caller-allocates=1 . This would leave the ownership to the caller, and remove a whole lot of uncertainty.

    Various new (comparatively speaking) API allow the caller to pass an unitialised GValue , and will leave initialisation to the caller, which is how it should be, but this kind of change isn’t always possible in a backward compatible way.

    Arrays

    You can use three types of C arrays in your API :

    • zero-terminated arrays, which are the easiest to use, especially for pointers and strings
    • fixed-size arrays
    • arrays with length arguments

    Addendum: strings and byte arrays

    A const char* argument for C strings with a length argument is not an array:

    /**
     * some_object_load_data:
     * @self: ...
     * @str: the data to load
     * @len: length of @str in bytes, or -1
     *
     * ...
     */
    void
    some_object_load_data (SomeObject *self,
                           const char *str,
                           ssize_t len)
    

    Never annotate the str argument with array length=len . Ideally, this kind of function should not exist in the first place . You should always use const char* for NUL -terminated strings, possibly UTF -8 encoded; if you allow embedded NUL characters then use a bytes array:

    /**
     * some_object_load_data:
     * @self: ...
     * @data: (array length=len) (element-type uint8): the data to load
     * @len: the length of the data in bytes
     *
     * ...
     */
    void
    some_object_load_data (SomeObject *self,
                           const unsigned char *data,
                           size_t len)
    

    Instead of unsigned char you can also use uint8_t , just to drive the point home.

    Yes, it’s slightly nicer to have a single entry point for strings and byte arrays, but that’s just a C convenience: decent languages will have a proper string type, which always comes with a length; and string types are not binary data.

    Addendum: GArray , GPtrArray , GByteArray

    Whatever you do, however low you feel on the day, whatever particular tragedy befell your family at some point, please: never use GLib array types in your API . Nothing good will ever come of it, and you’ll just spend your days regretting this choice.

    Yes: gobject-introspection transparently converts between GLib array types and C types, to the point of allowing you to annotate the contents of the array. The problem is that that information is static, and only exists at the introspection level. There’s nothing that prevents you from putting other random data into a GPtrArray , as long as it’s pointer-sized. There’s nothing that prevents a version of a library from saying that you own the data inside a GArray , and have the next version assign a clear function to the array to avoid leaking it all over the place on error conditions, or when using g_autoptr .

    Adding support for GLib array types in the introspection was a well-intentioned mistake that worked in very specific cases—for instance, in a library that is private to an application. Any well-behaved, well-designed general purpose library should not expose this kind of API to its consumers.

    You should use GArray , GPtrArray , and GByteArray internally; they are good types, and remove a lot of the pain of dealing with C arrays. Those types should never be exposed at the API boundary: always convert them to C arrays, or wrap them into your own data types, with proper argument validation and ownership rules.

    Addendum: GHashTable

    What’s worse than a type that contains data with unclear ownership rules decided at run time? A type that contains twice the amount of data with unclear ownership rules decided at run time.

    Just like the GLib array types, hash tables should be used but never directly exposed to consumers of an API .

    Addendum: GList , GSList , GQueue

    See above, re: pain and misery. On top of that, linked lists are a terrible data type that people should rarely, if ever, use in the first place.

    Callbacks

    Your callbacks should always be in the form of a simple callable with a data argument:

    typedef void (* SomeCallback) (SomeObject *obj,
                                   gpointer data);
    

    Any function that takes a callback should also take a “user data” argument that will be passed as is to the callback:

    // scope: call; the callback data is valid until the
    // function returns
    void
    some_object_do_stuff_immediately (SomeObject *self,
                                      SomeCallback callback,
                                      gpointer data);
    
    // scope: notify; the callback data is valid until the
    // notify function gets called
    void
    some_object_do_stuff_with_a_delay (SomeObject *self,
                                       SomeCallback callback,
                                       gpointer data,
                                       GDestroyNotify notify);
    
    // scope: async; the callback data is valid until the async
    // callback is called
    void
    some_object_do_stuff_but_async (SomeObject *self,
                                    GCancellable *cancellable,
                                    GAsyncReadyCallback callback,
                                    gpointer data);
    
    // not pictured here: scope forever; the data is valid fori
    // the entirety of the process lifetime
    

    If your function takes more than one callback argument, you should make sure that it also takes a different user data for each callback, and that the lifetime of the callbacks are well defined. The alternative is to use GClosure instead of a simple C function pointer—but that comes at a cost of GValue marshalling, so the recommendation is to stick with one callback per function.

    Addendum: the closure annotation

    It seems that many people are unclear about the closure annotation.

    Whenever you’re describing a function that takes a callback, you should always annotate the callback argument with the argument that contains the user data using the (closure argument) annotation, e.g.

    /**
     * some_object_do_stuff_immediately:
     * @self: ...
     * @callback: (scope call) (closure data): the callback
     * @data: the data to be passed to the @callback
     *
     * ...
     */
    

    You should not annotate the data argument with a unary (closure) .

    The unary (closure) is meant to be used when annotating the callback type :

    /**
     * SomeCallback:
     * @self: ...
     * @data: (closure): ...
     *
     * ...
     */
    typedef void (* SomeCallback) (SomeObject *self,
                                   gpointer data);
    

    Yes, it’s confusing, I know.

    Sadly, the introspection parser isn’t very clear about this, but in the future it will emit a warning if it finds a unary closure on anything that isn’t a callback type.

    Ideally, you don’t really need to annotate anything when you call your argument user_data , but it does not hurt to be explicit.


    A cleaned up version of this blog post will go up on the gobject-introspection website, and we should really have a proper set of best API design practices on the Developer Documentation website by now; nevertheless, I do hope people will actually follow these recommendations at some point, and that they will be prepared for new recommendations in the future. Only dead and unmaintained projects don’t change, after all, and I expect the GNOME stack to last a bit longer than the 25 years it already spans today.

    • wifi_tethering open_in_new

      This post is public

      www.bassi.io /articles/2023/02/20/bindable-api-2023/

    • chevron_right

      Alberto Ruiz: Dilemma’s in Rust Land: porting a GNOME library to Rust

      news.movim.eu / PlanetGnome • 19 February, 2023 • 2 minutes

    It has been a while since my last post, so I figured I just picked up a topic that has been around my mind lately.

    After I ported the RSVG Pixbuf Loader to Rust (although I gave up the meson-fu to Federico and Bilal) I decided that maybe I should give a try at porting the WebP Pixbuf Loader .

    webp-pixbuf-loader is probably the only FOSS project I have started on my own that I have managed to maintain in the long run without orphaning or giving it up to someone else. I wrote it out of curiosity and it turns out plenty of people find it rather useful as webp is pretty popular for storing comic book backups and other media.

    The WebP Pixbuf Loader is relatively small although ever since animation support was contributed it grew quite a bit. I’ve been handling a couple of issues ranging from endianness to memory leaks, I thought it was probably worth the while to give it some Rusty love.

    Porting the static image support was relatively quick, there’s, but it took me a while to understand how animation works in GDK-Pixbuf as the original animation support in C was contributed by alanhaw .

    I suspect I am prolly the first person to use the GdkPixbufLoader APIs to implement a new pixbuf loader, I had request a few fixes upstream, kudos to Sebastian Dröge and Bilal for handling those swiftly and shipping them in the last release.

    Anyhow, last month I finally made it all work :

    Hesitations

    Now comes the hesitation part, regardless of integrating the former tests in the build system (Cargo is great at embedded unit testing but meson is better at running an integration test plan), my main gripe is that it turns out that there’s quite a few people packaging this, not just for Linux distros but also BSDs, Illumos, Windows and brew/macOS…

    I really don’t know what the impact would be for anyone packaging outside of the Linux world, I have a few CI pipelines for Windows but obviously I am about to break them if I switch.

    I am pondering the idea of releasing a bunch of beta releases and hoping package maintainers will start taking notice that I’m moving on, but I am trying to be mindful of how much time they need to sink for the Rust move to happen and weight that against the net benefit.

    The other part that makes me hesitate over flipping the switch is the measure of the overall benefit. Sure Rust is nicer to maintain, but it still is a small codebase, Rust adds a bunch of runtime dependencies (bindings) and it is not clear to me how healthy the webp bindings are going to be long term, there are two similarly named bindings one has more frequent releases and the other is more complete which is annoying. These bindings bring an issue of size: the Rust port is 4MB in size vs. 76KB for the C implementation.

    Not sure what to do, feel free to add your thoughts in the comment section.

    • chevron_right

      Jussi Pakkanen: PDF output in images

      news.movim.eu / PlanetGnome • 17 February, 2023 • 2 minutes

    Generating PDF files is mostly ( but not entirely ) a serialization problem where you keep repeating the following loop:

    • Find out what functionality PDF has
    • Read the specification to find out how it is expressed using PDF document syntax
    • Come up with some sort of an API to express same
    • Serialize the latter into the former
    • Debug

    This means that you have to spend a fair bit of time without much to show for it apart from documents with various black boxes in them. However once you have enough foundational code, then suddenly you can generate all sorts of fun images. Let's look at some now.

    Paths are easy to define with lines, beziers and the like, as are path paint styles like line caps and joints. Choosing between nonzero and even-odd winding rules is just a question of choosing a different paint operator.

    PDF allows you to set any draw object as a "clipping path" which behaves like a stencil. Subsequent drawing operations are only applied to those pixels that are inside the specified clipping area. The painting model is uniform, text and paths are mostly interchangeable so text can be used as a clipping path. The gradient is a PNG image, not a vector object.

    This color wheel looks fairly average, but it is defined in L*a*b* color space . Did you know that PDF has native support for L*a*b* colors without needing any ICC profiles? I sure didn't until I read the spec.

    And finally here are some shadings and patterns. The first two are your standard linear and spherical gradients, but the latter two are more interesting. In PDF you can specify a pattern, which is basically just a rectangular area. You can draw on it with almost all the same operators as on a page (you can't use patterns within patterns, though). You can then use said pattern to paint other objects and the PDF renderer will fill the space by tiling the pattern (yes, of course there is a transformation matrix you can specify). As text is not special you can draw a single character and fill it with a repeated instance of a different character.

    Using it in Python

    The code needed to generate an empty PDF document looks approximately like this:

    import a4pdf
    o = a4pdf.Options()
    g = a4pdf.Generator('out.pdf', o)
    with g.page_draw_context() as ctx:
    # Drawing commands would go here.
    pass

    This snippet utilizes almost 100% of all available API thus far. So there's not much you can do with it yet.

    • chevron_right

      Jiri Eischmann: How is Linux used by FIT BUT students

      news.movim.eu / PlanetGnome • 17 February, 2023 • 2 minutes

    The Faculty of Information Technology of Brno University of Technology is one of the two top computer science schools in Brno, Czech Republic. Our development office of Red Hat has intensive cooperation with them including educating students about Linux and open source software. To find out more about how they use Linux, we ran a survey that collected answers from 176 students which is a pretty good sample. I promised to share results publicly, so here they are:

    The following chart shows the distribution of responders by year of school. The survey was primarily targeting students in the first year which is why they make up over 50% of the responses.

    The following chart shows how many students had experience with a Linux distribution prior their studies at the university. 46% did which shows a pretty good exposure to Linux at high schools.

    And now what desktop OS students use primarily . Windows are dominating, but Linux is used as a primary OS by roughly one third of students. macOS is only at 10%. Although we gave responders an option to specify other OSes, no one submitted, for example, BSD.

    The following chart shows in what form students use Linux primarily (as either a primary or secondary OS). 44% of students have it installed on their desktop/laptop. 31% use Windows Subsystem for Linux. School programming assignments have to run on Linux, so if they want to stick with Windows, WSL is the easiest way for them. Virtualization is at 9% and remote server at 13% (I suspect it’s mostly uni servers where students can test their assignments before submission).

    And here come shares of Linux distributions. Responders could pick multiple options, so the total is over 100%. Basically the only relevant distributions among FIT BUT students are Ubuntu, Fedora, Arch Linux and Debian.

    Ubuntu has a clear lead. It’s the default option for WSL where it is on vast majority of installations, so I wondered what the share would be without WSL.

    Without WSL the gap between Ubuntu and the rest of the pack is smaller. And since I’m from the Red Hat desktop team I also wondered what are the shares among students who indicated they use Linux primarily on desktop/laptop.

    When it comes to desktop computers and laptops the shares of Fedora and Ubuntu are almost the same. That shows two things: 1. Fedora is strong on the desktop among local students, 2. being the default option in WSL gives Ubuntu an advantage in mindshare. Fedora is not even officially available for WSL, but even if it was, it wouldn’t probably change much because other distros are available in the Microsoft Store and only one student of out 50+ who primarily use WSL responded that they use something else than Ubuntu. WSL is probably used by users who want some Linux in their Windows and don’t care much which one it is, so they stay with the default.

    We also asked students what prevents them from using Linux primarily. By far the most frequent answer (80%) was “Software I use is not available for Linux”, followed by “I don’t like the UX and logic of the OS” (28%) and “Compatibility with my hardware” (11%). Some students also responded that they simply hadn’t had enough time to get familiar with Linux and are staying with what they know. Other reasons were marginal.

    • wifi_tethering open_in_new

      This post is public

      eischmann.wordpress.com /2023/02/17/how-is-linux-used-by-fit-but-students/

    • chevron_right

      Sam Thursfield: Status update, 17/02/2022

      news.movim.eu / PlanetGnome • 17 February, 2023 • 2 minutes

    This month I attended FOSDEM for the first time since 2017. In addition to eating 4 delicious waffles, I had the honour of presenting two talks, the first in the Testing & Automation devroom on Setting up OpenQA testing for GNOME .

    GNOME’s initial OpenQA testing is mostly implemented now and it’s already found its first real bug . The next step is getting more folk interested within GNOME, so we can ensure ongoing maintenance of the tests and infra, and ensure a bus factor of > 1. If you see me at GUADEC then I will probably talk to you about OpenQA, be prepared!! 🙂

    My second talk was in the Python devroom, on DIY music recommendations . I intermittently develop a set of playlist generation tools named Calliope , and this talk was mostly aiming to inspire people to start similar fun & small projects, using simple AI techniques that you can learn in a weekend, and taking advantage of the amazing resource that is Musicbrainz. It seemed to indeed inspire some of the audience and led to an interesting chat with Rob Kaye of the Metabrainz Foundation – there is more cool stuff on the way from them.

    Here’s a fantastic sketch of the talk by Jeroen Heijmans :

    Talk summary sketch, CC BY-SA 4.0

    I didn’t link to this in the talk, but apropos of nothing here’s an interesting video entitled Why Spotify Will Eventually Fail .

    On the Saturday I met up with Carlos Garnacho and gatecrashed the GNOME docs hackfest , discussing various improvements around search in GNOME. Most of these are now waiting for developer time as they are too large to be done in occasional moments of evening and weekend downtime, get in touch if you want to find out more!

    I must also shout out Marco Trevisan for showing me where to get a decent meal near Madrid Chamartín station on the way home.

    Meanwhile at Codethink I have been getting more involved in marketing. Its a company that exists in two worlds, commercial software services on one side and community-driven open source software on the other, often trying our best to build bridges between the two. There aren’t many marketing graduates who are experts in open source, and neither many experience software developers who want to work fulltime on managing social media, so we are still figuring out the details…

    Anyway, the initial outcome is that Codethink is now on the Fediverse – follow us here! @codethink@social.codethink.co.uk