NanoMichael / MicroTeX

A dynamic, cross-platform, and embeddable LaTeX rendering library
MIT License
418 stars 69 forks source link

Create "cairo" platform seperate from "cairomm" #96

Closed sp1ritCS closed 2 years ago

sp1ritCS commented 2 years ago

This implements a cairo platform that takes an cairo_t* pointer. The problem with cairomm is, that there was an API change between cairomm and pangomm for gtkmm-3.0 and gtkmm-4.0. Yet there wasn't such a change in the underlaying c libs. (while cairomm-1.0 is now cairomm-1.16 and pangomm-1.4 is now pangomm-2.48; cairo is linked versionless and pango is 1.0).

This is also interesting, given that you implemented a clm c api for wasm, I wonder if this could be extended to the cairo api aswell.

NanoMichael commented 2 years ago

Maybe we only take the pure C version of cairo into account? Since the cairomm is also based on cairo.

This is also interesting, given that you implemented a clm c api for wasm, I wonder if this could be extended to the cairo api aswell.

Excellent advice. Or do we just provide the pure C API to the outside world? That's simple enough and we can hide all the implementation details from the users.

sp1ritCS commented 2 years ago

Maybe we only take the pure C version of cairo into account? Since the cairomm is also based on cairo.

yeah, I kinda wanted to retain compatibility with previous versions, but given that one would have to additionally include the clatexmath-cairo library and change the graphics_cairo.h to graphics_cairomm.h anyways, I believe adding ->cobj() to all constructor calls of Graphics2D_cairo will be fine.

I'm also not quite sure if I managed to do ownership correctly, mostly due to the way how cairo implementation puts fontfaces and the image context into global scope (see the cairopp::{CairoFontFacePtr, CairoCtxPtr} shared pointers I had to implement).

Or do we just provide the pure C API to the outside world? That's simple enough and we can hide all the implementation details from the users.

yes, but how would the construction of Graphics2D_impl look like?

NanoMichael commented 2 years ago

yes, but how would the construction of Graphics2D_impl look like?

:thinking: I got an idea but looks quite tricky.

First, we expose a plain old struct to the user side:

struct Graphics2DContext {
  void* impl; // The implementation on user side
};

Second, the user needs to register drawing callbacks to the library:

// ... code in library
void (*g_drawLine)(Graphics2DContext, float, float, float, float);

// the C API
extern "C" {
void setDrawLineFunction(void (*drawLine)(Graphics2DContext*, float, float, float, float)) {
  g_drawLine = drawLine;
}

// ... other drawing functions
}

Third, implement the interface Graphics2D:

class Graphics2D_impl: public Graphics2D {
  Graphics2DContext* _ctx;

  Graphics2D_impl(Graphics2DContext* ctx): _ctx(ctx) {}

  void drawLine(float x1, float y1, float x2, float y2) override {
    g_drawLine(_ctx, x1, y1, x2, y2);
  }

  // ... other implementations
};

And the formula drawing should be changed to:

struct PaintableFormula {
    void* render; // The Render object
};

// the C API
extern "C" {
  PaintableFormula* parse(const char* latex) {
    auto render = LaTeX::parse(...);
    return { (void*) render };
  }

  void draw(Graphics2DContext* ctx, PaintableFormula* formula, float x, float y) {
    auto g2d = Graphics2D_impl(ctx);
    auto render = reinterpret_cast<Render*>(formula->render);
    render->draw(g2d, x, y);
  }
}

This seems can solve the problem, but that is quite cumbersome for the user to register these drawing functions...