/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * PikaText-vectors * Copyright (C) 2003 Sven Neumann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include #include "text-types.h" #include "core/pika.h" #include "core/pikaimage.h" #include "vectors/pikabezierstroke.h" #include "vectors/pikavectors.h" #include "vectors/pikaanchor.h" #include "pikatext.h" #include "pikatext-vectors.h" #include "pikatextlayout.h" #include "pikatextlayout-render.h" typedef struct { PikaVectors *vectors; PikaStroke *stroke; PikaAnchor *anchor; } RenderContext; static void pika_text_render_vectors (cairo_t *cr, RenderContext *context); PikaVectors * pika_text_vectors_new (PikaImage *image, PikaText *text) { PikaVectors *vectors; RenderContext context = { NULL, }; g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); g_return_val_if_fail (PIKA_IS_TEXT (text), NULL); vectors = pika_vectors_new (image, NULL); if (text->text || text->markup) { PikaTextLayout *layout; cairo_surface_t *surface; cairo_t *cr; gdouble xres; gdouble yres; GError *error = NULL; if (text->text) pika_object_set_name_safe (PIKA_OBJECT (vectors), text->text); context.vectors = vectors; surface = cairo_recording_surface_create (CAIRO_CONTENT_ALPHA, NULL); cr = cairo_create (surface); pika_image_get_resolution (image, &xres, &yres); layout = pika_text_layout_new (text, xres, yres, &error); if (error) { pika_message_literal (image->pika, NULL, PIKA_MESSAGE_ERROR, error->message); g_error_free (error); } pika_text_layout_render (layout, cr, text->base_dir, TRUE); g_object_unref (layout); pika_text_render_vectors (cr, &context); cairo_destroy (cr); cairo_surface_destroy (surface); if (context.stroke) pika_stroke_close (context.stroke); } return vectors; } static inline void pika_text_vector_coords (const double x, const double y, PikaCoords *coords) { const PikaCoords default_values = PIKA_COORDS_DEFAULT_VALUES; *coords = default_values; coords->x = x; coords->y = y; } static gint moveto (RenderContext *context, const double x, const double y) { PikaCoords start; #if PIKA_TEXT_DEBUG g_printerr ("moveto %f, %f\n", x, y); #endif pika_text_vector_coords (x, y, &start); if (context->stroke) pika_stroke_close (context->stroke); context->stroke = pika_bezier_stroke_new_moveto (&start); pika_vectors_stroke_add (context->vectors, context->stroke); g_object_unref (context->stroke); return 0; } static gint lineto (RenderContext *context, const double x, const double y) { PikaCoords end; #if PIKA_TEXT_DEBUG g_printerr ("lineto %f, %f\n", x, y); #endif if (! context->stroke) return 0; pika_text_vector_coords (x, y, &end); pika_bezier_stroke_lineto (context->stroke, &end); return 0; } static gint cubicto (RenderContext *context, const double x1, const double y1, const double x2, const double y2, const double x3, const double y3) { PikaCoords control1; PikaCoords control2; PikaCoords end; #if PIKA_TEXT_DEBUG g_printerr ("cubicto %f, %f\n", x3, y3); #endif if (! context->stroke) return 0; pika_text_vector_coords (x1, y1, &control1); pika_text_vector_coords (x2, y2, &control2); pika_text_vector_coords (x3, y3, &end); pika_bezier_stroke_cubicto (context->stroke, &control1, &control2, &end); return 0; } static gint closepath (RenderContext *context) { #if PIKA_TEXT_DEBUG g_printerr ("moveto\n"); #endif if (! context->stroke) return 0; pika_stroke_close (context->stroke); context->stroke = NULL; return 0; } static void pika_text_render_vectors (cairo_t *cr, RenderContext *context) { cairo_path_t *path; gint i; path = cairo_copy_path (cr); for (i = 0; i < path->num_data; i += path->data[i].header.length) { cairo_path_data_t *data = &path->data[i]; /* if the drawing operation is the final moveto of the glyph, * break to avoid creating an empty point. This is because cairo * always adds a moveto after each closepath. */ if (i + data->header.length >= path->num_data) break; switch (data->header.type) { case CAIRO_PATH_MOVE_TO: moveto (context, data[1].point.x, data[1].point.y); break; case CAIRO_PATH_LINE_TO: lineto (context, data[1].point.x, data[1].point.y); break; case CAIRO_PATH_CURVE_TO: cubicto (context, data[1].point.x, data[1].point.y, data[2].point.x, data[2].point.y, data[3].point.x, data[3].point.y); break; case CAIRO_PATH_CLOSE_PATH: closepath (context); break; } } cairo_path_destroy (path); }