diff options
-rw-r--r-- | data/de.marvinborner.zathura-note.metainfo.xml | 1 | ||||
-rw-r--r-- | license | 13 | ||||
-rw-r--r-- | meson.build | 5 | ||||
-rw-r--r-- | zathura-note/cairo_jpg.c | 612 | ||||
-rw-r--r-- | zathura-note/cairo_jpg.h | 75 | ||||
-rw-r--r-- | zathura-note/note.c | 139 |
6 files changed, 117 insertions, 728 deletions
diff --git a/data/de.marvinborner.zathura-note.metainfo.xml b/data/de.marvinborner.zathura-note.metainfo.xml index 8958e32..3ca7771 100644 --- a/data/de.marvinborner.zathura-note.metainfo.xml +++ b/data/de.marvinborner.zathura-note.metainfo.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <component type="addon"> <id>de.marvinborner.zathura-note</id> + <project_license>WTFPL</project_license> <extends>org.pwmt.zathura</extends> <name>Zathura-note</name> <summary>Notability .note support for zathura</summary> @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2021 Marvin Borner <develop@marvinborner.de> + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/meson.build b/meson.build index 5683db2..8f7647f 100644 --- a/meson.build +++ b/meson.build @@ -21,7 +21,6 @@ glib = dependency('glib-2.0') cairo = dependency('cairo') zip = dependency('libzip') plist = dependency('libplist') -jpeg = dependency('libjpeg') build_dependencies = [ zathura, @@ -29,8 +28,7 @@ build_dependencies = [ glib, cairo, zip, - plist, - jpeg + plist ] plugindir = zathura.get_pkgconfig_variable('plugindir') @@ -55,7 +53,6 @@ flags = [ flags = cc.get_supported_arguments(flags) sources = files( - 'zathura-note/cairo_jpg.c', 'zathura-note/plugin.c', 'zathura-note/note.c' ) diff --git a/zathura-note/cairo_jpg.c b/zathura-note/cairo_jpg.c deleted file mode 100644 index cd47f20..0000000 --- a/zathura-note/cairo_jpg.c +++ /dev/null @@ -1,612 +0,0 @@ -/* Copyright 2018 Bernhard R. Fischer, 4096R/8E24F29D <bf@abenteuerland.at> - * - * This file is part of Cairo_JPG. - * - * Cairo_JPG is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Cairo_JPG 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Cairo_JPG. If not, see <https://www.gnu.org/licenses/>. - */ - -/*! \file cairo_jpg.c - * This file contains two functions for reading and writing JPEG files from - * and to Cairo image surfaces. It uses the functions from the libjpeg. - * Most of the code is directly derived from the online example at - * http://libjpeg-turbo.virtualgl.org/Documentation/Documentation - * - * All prototypes are defined in cairo_jpg.h All functions and their parameters - * and return values are described below directly at the functions. You may - * also have a look at the preprocessor macros defined below. - * - * To compile this code you need to have installed the packages libcairo2-dev - * and libjpeg-dev. Compile with the following to create an object file to link - * with your code: - * gcc -std=c99 -Wall -c `pkg-config cairo libjpeg --cflags --libs` cairo_jpg.c - * Use the following command to include the main() function and create an - * executable for testing of this code: - * gcc -std=c99 -Wall -o cairo_jpg -DCAIRO_JPEG_MAIN `pkg-config cairo libjpeg --cflags --libs` cairo_jpg.c - * - * @author Bernhard R. Fischer, 4096R/8E24F29D bf@abenteuerland.at - * @version 2020/01/18 - * @license LGPL3. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include <fcntl.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <cairo.h> -#include <jpeglib.h> - -#include "cairo_jpg.h" - -/*! Macro to activate main() function. This is only used for testing. Comment - * it out (#undef) if you link this file to your own program. - */ -//#define CAIRO_JPEG_MAIN -// -/*! Define this to use an alternate implementation of - * cairo_image_surface_create_from_jpeg() which fstat(3)s the file before - * reading (see below). For huge files this /may/ be slightly faster. - */ -#undef CAIRO_JPEG_USE_FSTAT - -/*! This is the read block size for the stream reader - * cairo_image_surface_create_from_jpeg_stream(). - */ -#ifdef USE_CAIRO_READ_FUNC_LEN_T -#define CAIRO_JPEG_IO_BLOCK_SIZE 4096 -#else -/*! Block size has to be one if cairo_read_func_t is in use because of the lack - * to detect EOF (truncated reads). - */ -#define CAIRO_JPEG_IO_BLOCK_SIZE 1 -/*! In case of original cairo_read_func_t is used fstat() should be used for - * performance reasons (see CAIRO_JPEG_USE_FSTAT above). - */ -#define CAIRO_JPEG_USE_FSTAT -#endif - -/*! Define this to test jpeg creation with non-image surfaces. This is only for - * testing and is to be used together with CAIRO_JPEG_MAIN. - */ -#undef CAIRO_JPEG_TEST_SIMILAR -#if defined(CAIRO_JPEG_TEST_SIMILAR) && defined(CAIRO_JPEG_MAIN) -#include <cairo-pdf.h> -#endif - -#ifndef LIBJPEG_TURBO_VERSION -/*! This function makes a covnersion for "odd" pixel sizes which typically is a - * conversion from a 3-byte to a 4-byte (or more) pixel size or vice versa. - * The conversion is done from the source buffer src to the destination buffer - * dst. The caller MUST ensure that src and dst have the correct memory size. - * This is dw * num for dst and sw * num for src. src and dst may point to the - * same memory address. - * @param dst Pointer to destination buffer. - * @param dw Pixel width (in bytes) of pixels in destination buffer, dw >= 3. - * @param src Pointer to source buffer. - * @param sw Pixel width (in bytes) of pixels in source buffer, sw >= 3. - * @param num Number of pixels to convert, num >= 1; - */ -static void pix_conv(unsigned char *dst, int dw, const unsigned char *src, int sw, int num) -{ - int si, di; - - // safety check - if (dw < 3 || sw < 3 || dst == NULL || src == NULL) - return; - - num--; - for (si = num * sw, di = num * dw; si >= 0; si -= sw, di -= dw) { -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - dst[di + 2] = src[si]; - dst[di + 1] = src[si + 1]; - dst[di + 0] = src[si + 2]; -#else - // FIXME: This is untested, it may be wrong. - dst[di - 3] = src[si - 3]; - dst[di - 2] = src[si - 2]; - dst[di - 1] = src[si - 1]; -#endif - } -} -#endif - -/*! This function creates a JPEG file in memory from a Cairo image surface. - * @param sfc Pointer to a Cairo surface. It should be an image surface of - * either CAIRO_FORMAT_ARGB32 or CAIRO_FORMAT_RGB24. Other formats are - * converted to CAIRO_FORMAT_RGB24 before compression. - * Please note that this may give unexpected results because JPEG does not - * support transparency. Thus, default background color is used to replace - * transparent regions. The default background color is black if not specified - * explicitly. Thus converting e.g. PDF surfaces without having any specific - * background color set will apear with black background and not white as you - * might expect. In such cases it is suggested to manually convert the surface - * to RGB24 before calling this function. - * @param data Pointer to a memory pointer. This parameter receives a pointer - * to the memory area where the final JPEG data is found in memory. This - * function reserves the memory properly and it has to be freed by the caller - * with free(3). - * @param len Pointer to a variable of type size_t which will receive the final - * lenght of the memory buffer. - * @param quality Compression quality, 0-100. - * @return On success the function returns CAIRO_STATUS_SUCCESS. In case of - * error CAIRO_STATUS_INVALID_FORMAT is returned. - */ -cairo_status_t cairo_image_surface_write_to_jpeg_mem(cairo_surface_t *sfc, unsigned char **data, - size_t *len, int quality) -{ - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - JSAMPROW row_pointer[1]; - cairo_surface_t *other = NULL; - - // check valid input format (must be IMAGE_SURFACE && (ARGB32 || RGB24)) - if (cairo_surface_get_type(sfc) != CAIRO_SURFACE_TYPE_IMAGE || - (cairo_image_surface_get_format(sfc) != CAIRO_FORMAT_ARGB32 && - cairo_image_surface_get_format(sfc) != CAIRO_FORMAT_RGB24)) { - // create a similar surface with a proper format if supplied input format - // does not fulfill the requirements - double x1, y1, x2, y2; - other = sfc; - cairo_t *ctx = cairo_create(other); - // get extents of original surface - cairo_clip_extents(ctx, &x1, &y1, &x2, &y2); - cairo_destroy(ctx); - - // create new image surface - sfc = cairo_surface_create_similar_image(other, CAIRO_FORMAT_RGB24, x2 - x1, - y2 - y1); - if (cairo_surface_status(sfc) != CAIRO_STATUS_SUCCESS) - return CAIRO_STATUS_INVALID_FORMAT; - - // paint original surface to new surface - ctx = cairo_create(sfc); - cairo_set_source_surface(ctx, other, 0, 0); - cairo_paint(ctx); - cairo_destroy(ctx); - } - - // finish queued drawing operations - cairo_surface_flush(sfc); - - // init jpeg compression structures - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - - // set compression parameters - jpeg_mem_dest(&cinfo, data, len); - cinfo.image_width = cairo_image_surface_get_width(sfc); - cinfo.image_height = cairo_image_surface_get_height(sfc); -#ifdef LIBJPEG_TURBO_VERSION -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - //cinfo.in_color_space = JCS_EXT_BGRX; - cinfo.in_color_space = cairo_image_surface_get_format(sfc) == CAIRO_FORMAT_ARGB32 ? - JCS_EXT_BGRA : - JCS_EXT_BGRX; -#else - //cinfo.in_color_space = JCS_EXT_XRGB; - cinfo.in_color_space = cairo_image_surface_get_format(sfc) == CAIRO_FORMAT_ARGB32 ? - JCS_EXT_ARGB : - JCS_EXT_XRGB; -#endif - cinfo.input_components = 4; -#else - cinfo.in_color_space = JCS_RGB; - cinfo.input_components = 3; -#endif - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE); - - // start compressor - jpeg_start_compress(&cinfo, TRUE); - - // loop over all lines and compress - while (cinfo.next_scanline < cinfo.image_height) { -#ifdef LIBJPEG_TURBO_VERSION - row_pointer[0] = cairo_image_surface_get_data(sfc) + - (cinfo.next_scanline * cairo_image_surface_get_stride(sfc)); -#else - unsigned char row_buf[3 * cinfo.image_width]; - pix_conv(row_buf, 3, - cairo_image_surface_get_data(sfc) + - (cinfo.next_scanline * cairo_image_surface_get_stride(sfc)), - 4, cinfo.image_width); - row_pointer[0] = row_buf; -#endif - (void)jpeg_write_scanlines(&cinfo, row_pointer, 1); - } - - // finalize and close everything - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); - - // destroy temporary image surface (if available) - if (other != NULL) - cairo_surface_destroy(sfc); - - return CAIRO_STATUS_SUCCESS; -} - -/*! This is the internal write function which is called by - * cairo_image_surface_write_to_jpeg(). It is not exported. - */ -static cairo_status_t cj_write(void *closure, const unsigned char *data, unsigned int length) -{ - return write((intptr_t)closure, data, length) < length ? CAIRO_STATUS_WRITE_ERROR : - CAIRO_STATUS_SUCCESS; -} - -/*! This function writes JPEG file data from a Cairo image surface by using the - * user-supplied stream writer function write_func(). - * @param sfc Pointer to a Cairo *image* surface. Its format must either be - * CAIRO_FORMAT_ARGB32 or CAIRO_FORMAT_RGB24. Other formats are not supported - * by this function, yet. - * @param write_func Function pointer to a function which is actually writing - * the data. - * @param closure Pointer to user-supplied variable which is directly passed to - * write_func(). - * @param quality Compression quality, 0-100. - * @return This function calles cairo_image_surface_write_to_jpeg_mem() and - * returns its return value. - */ -cairo_status_t cairo_image_surface_write_to_jpeg_stream(cairo_surface_t *sfc, - cairo_write_func_t write_func, - void *closure, int quality) -{ - cairo_status_t e; - unsigned char *data = NULL; - size_t len = 0; - - // create JPEG data in memory from surface - if ((e = cairo_image_surface_write_to_jpeg_mem(sfc, &data, &len, quality)) != - CAIRO_STATUS_SUCCESS) - return e; - - // write whole memory block with stream function - e = write_func(closure, data, len); - - // free JPEG memory again and return the return value - free(data); - return e; -} - -/*! This function creates a JPEG file from a Cairo image surface. - * @param sfc Pointer to a Cairo *image* surface. Its format must either be - * CAIRO_FORMAT_ARGB32 or CAIRO_FORMAT_RGB24. Other formats are not supported - * by this function, yet. - * @param filename Pointer to the filename. - * @param quality Compression quality, 0-100. - * @return In case of success CAIRO_STATUS_SUCCESS is returned. If an error - * occured while opening/creating the file CAIRO_STATUS_DEVICE_ERROR is - * returned. The error can be tracked down by inspecting errno(3). The function - * internally calles cairo_image_surface_write_to_jpeg_stream() and returnes - * its return value respectively (see there). - */ -cairo_status_t cairo_image_surface_write_to_jpeg(cairo_surface_t *sfc, const char *filename, - int quality) -{ - cairo_status_t e; - int outfile; - - // Open/create new file - if ((outfile = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == - -1) - return CAIRO_STATUS_DEVICE_ERROR; - - // write surface to file - e = cairo_image_surface_write_to_jpeg_stream(sfc, cj_write, (void *)(intptr_t)outfile, - quality); - - // close file again and return - close(outfile); - return e; -} - -/*! This function decompresses a JPEG image from a memory buffer and creates a - * Cairo image surface. - * @param data Pointer to JPEG data (i.e. the full contents of a JPEG file read - * into this buffer). - * @param len Length of buffer in bytes. - * @return Returns a pointer to a cairo_surface_t structure. It should be - * checked with cairo_surface_status() for errors. - */ -cairo_surface_t *cairo_image_surface_create_from_jpeg_mem(void *data, size_t len) -{ - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - JSAMPROW row_pointer[1]; - cairo_surface_t *sfc; - - // initialize jpeg decompression structures - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - jpeg_mem_src(&cinfo, data, len); - (void)jpeg_read_header(&cinfo, TRUE); - -#ifdef LIBJPEG_TURBO_VERSION -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - cinfo.out_color_space = JCS_EXT_BGRA; -#else - cinfo.out_color_space = JCS_EXT_ARGB; -#endif -#else - cinfo.out_color_space = JCS_RGB; -#endif - - // start decompressor - (void)jpeg_start_decompress(&cinfo); - - // create Cairo image surface - sfc = cairo_image_surface_create(CAIRO_FORMAT_RGB24, cinfo.output_width, - cinfo.output_height); - if (cairo_surface_status(sfc) != CAIRO_STATUS_SUCCESS) { - jpeg_destroy_decompress(&cinfo); - return sfc; - } - - // loop over all scanlines and fill Cairo image surface - while (cinfo.output_scanline < cinfo.output_height) { - unsigned char *row_address = - cairo_image_surface_get_data(sfc) + - (cinfo.output_scanline * cairo_image_surface_get_stride(sfc)); - row_pointer[0] = row_address; - (void)jpeg_read_scanlines(&cinfo, row_pointer, 1); -#ifndef LIBJPEG_TURBO_VERSION - pix_conv(row_address, 4, row_address, 3, cinfo.output_width); -#endif - } - - // finish and close everything - cairo_surface_mark_dirty(sfc); - (void)jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - - // set jpeg mime data - cairo_surface_set_mime_data(sfc, CAIRO_MIME_TYPE_JPEG, data, len, free, data); - - return sfc; -} - -/*! This function reads a JPEG image from a stream and creates a Cairo image - * surface. - * @param read_func Pointer to function which reads data. - * @param closure Pointer which is passed to read_func(). - * @return Returns a pointer to a cairo_surface_t structure. It should be - * checked with cairo_surface_status() for errors. - * @note If the surface returned is invalid you can use errno(3) to determine - * further reasons. Errno is set according to realloc(3). If you - * intend to check errno you shall set it to 0 before calling this function - * because it modifies errno only in case of an error. - */ -#ifdef USE_CAIRO_READ_FUNC_LEN_T -cairo_surface_t *cairo_image_surface_create_from_jpeg_stream(cairo_read_func_len_t read_func, - void *closure) -#else -cairo_surface_t *cairo_image_surface_create_from_jpeg_stream(cairo_read_func_t read_func, - void *closure) -#endif -{ - cairo_surface_t *sfc; - void *data, *tmp; - ssize_t len, rlen; - int eof = 0; - - // read all data into memory buffer in blocks of CAIRO_JPEG_IO_BLOCK_SIZE - for (len = 0, data = NULL; !eof; len += rlen) { - // grow memory buffer and check for error - if ((tmp = realloc(data, len + CAIRO_JPEG_IO_BLOCK_SIZE)) == NULL) - break; - data = tmp; - - // read bytes into buffer and check for error - rlen = read_func(closure, data + len, CAIRO_JPEG_IO_BLOCK_SIZE); -#ifdef USE_CAIRO_READ_FUNC_LEN_T - // check for error - if (rlen == -1) - break; - - // check if EOF occured - if (rlen < CAIRO_JPEG_IO_BLOCK_SIZE) - eof++; -#else - // check for error - if (rlen == CAIRO_STATUS_READ_ERROR) - eof++; -#endif - } - - // check for error in read loop - if (!eof) { - free(data); - return cairo_image_surface_create(CAIRO_FORMAT_INVALID, 0, 0); - } - - // call jpeg decompression and return surface - sfc = cairo_image_surface_create_from_jpeg_mem(data, len); - if (cairo_surface_status(sfc) != CAIRO_STATUS_SUCCESS) - free(data); - - return sfc; -} - -#ifdef CAIRO_JPEG_USE_FSTAT -/*! This function reads an JPEG image from a file an creates a Cairo image - * surface. Internally the filesize is determined with fstat(2) and then the - * whole data is read at once. - * @param filename Pointer to filename of JPEG file. - * @return Returns a pointer to a cairo_surface_t structure. It should be - * checked with cairo_surface_status() for errors. - * @note If the returned surface is invalid you can use errno to determine - * further reasons. Errno is set according to fopen(3) and malloc(3). If you - * intend to check errno you shall set it to 0 before calling this function - * because it does not modify errno itself. - */ -cairo_surface_t *cairo_image_surface_create_from_jpeg(const char *filename) -{ - void *data; - int infile; - struct stat stat; - - // open input file - if ((infile = open(filename, O_RDONLY)) == -1) - return cairo_image_surface_create(CAIRO_FORMAT_INVALID, 0, 0); - - // get stat structure for file size - if (fstat(infile, &stat) == -1) - return cairo_image_surface_create(CAIRO_FORMAT_INVALID, 0, 0); - - // allocate memory - if ((data = malloc(stat.st_size)) == NULL) - return cairo_image_surface_create(CAIRO_FORMAT_INVALID, 0, 0); - - // read data - if (read(infile, data, stat.st_size) < stat.st_size) - return cairo_image_surface_create(CAIRO_FORMAT_INVALID, 0, 0); - - close(infile); - - return cairo_image_surface_create_from_jpeg_mem(data, stat.st_size); -} - -#else - -/*! This is the read function which is called by - * cairo_image_surface_create_from_jpeg_stream() (non-fstat-version below). It - * is not exported. - */ -#ifdef USE_CAIRO_READ_FUNC_LEN_T -static ssize_t cj_read(void *closure, unsigned char *data, unsigned int length) -{ - return read((intptr_t)closure, data, length); -} -#else -static cairo_status_t cj_read(void *closure, unsigned char *data, unsigned int length) -{ - return read((intptr_t)closure, data, length) < length ? CAIRO_STATUS_READ_ERROR : - CAIRO_STATUS_SUCCESS; -} -#endif - -/*! This function reads an JPEG image from a file an creates a Cairo image - * surface. Internally the function calls - * cairo_image_surface_create_from_jpeg_stream() to actually read the data. - * @param filename Pointer to filename of JPEG file. - * @return Returns a pointer to a cairo_surface_t structure. It should be - * checked with cairo_surface_status() for errors. - * @note If the returned surface is invalid you can use errno to determine - * further reasons. Errno is set according to fopen(3) and malloc(3). If you - * intend to check errno you shall set it to 0 before calling this function - * because it does not modify errno itself. - */ -cairo_surface_t *cairo_image_surface_create_from_jpeg(const char *filename) -{ - cairo_surface_t *sfc; - int infile; - - // open input file - if ((infile = open(filename, O_RDONLY)) == -1) - return cairo_image_surface_create(CAIRO_FORMAT_INVALID, 0, 0); - - // call stream loading function - sfc = cairo_image_surface_create_from_jpeg_stream(cj_read, (void *)(intptr_t)infile); - close(infile); - - return sfc; -} - -#endif - -#ifdef CAIRO_JPEG_MAIN -#include <string.h> -#include <strings.h> - -int strrcasecmp(const char *s1, const char *s2) -{ - int off = (int)strlen(s1) - - (int)strlen(s2); // typecast size_t to int because size_t typically is unsigned - return strcasecmp(s1 + (off < 0 ? 0 : off), s2); -} - -/*! Main routine, only for testing. #undef CAIRO_JPEG_MAIN or simply delete - * this part if you link this file to your own program. - */ -int main(int argc, char **argv) -{ - cairo_surface_t *sfc; - -#ifndef CAIRO_JPEG_TEST_SIMILAR - if (argc < 3) { - fprintf(stderr, "usage: %s <infile> <outfile>\n", argv[0]); - return 1; - } - - // test input file type and read file - if (!strrcasecmp(argv[1], ".png")) { - // read PNG file - sfc = cairo_image_surface_create_from_png(argv[1]); - } else if (!strrcasecmp(argv[1], ".jpg")) { - // read JPEG file - sfc = cairo_image_surface_create_from_jpeg(argv[1]); - } else { - fprintf(stderr, "source file is neither JPG nor PNG\n"); - return 1; - } - - // check surface status - if (cairo_surface_status(sfc) != CAIRO_STATUS_SUCCESS) { - fprintf(stderr, "error loading image: %s", - cairo_status_to_string(cairo_surface_status(sfc))); - return 2; - } - - // test output file type and write file - if (!strrcasecmp(argv[2], ".png")) { - // write PNG file - cairo_surface_write_to_png(sfc, argv[2]); - } else if (!strrcasecmp(argv[2], ".jpg")) { - // write JPEG file - cairo_image_surface_write_to_jpeg(sfc, argv[2], 90); - } else { - fprintf(stderr, "destination file is neither JPG nor PNG\n"); - return 1; - } - - cairo_surface_destroy(sfc); - -#else - sfc = cairo_pdf_surface_create("xyz.pdf", 595.276, 841.890); - - cairo_t *ctx = cairo_create(sfc); - cairo_set_source_rgb(ctx, 1, 1, 1); - cairo_paint(ctx); - cairo_move_to(ctx, 100, 100); - cairo_set_source_rgb(ctx, 1, 0, 0); - cairo_set_line_width(ctx, 3); - cairo_line_to(ctx, 400, 400); - cairo_stroke(ctx); - cairo_destroy(ctx); - - cairo_image_surface_write_to_jpeg(sfc, "xyz.jpg", 90); - cairo_surface_destroy(sfc); -#endif - - return 0; -} - -#endif diff --git a/zathura-note/cairo_jpg.h b/zathura-note/cairo_jpg.h deleted file mode 100644 index 5467581..0000000 --- a/zathura-note/cairo_jpg.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright 2018 Bernhard R. Fischer, 4096R/8E24F29D <bf@abenteuerland.at> - * - * This file is part of Cairo_JPG. - * - * Cairo_JPG is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Cairo_JPG 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Cairo_JPG. If not, see <https://www.gnu.org/licenses/>. - */ - -#ifndef CAIRO_JPEG_H -#define CAIRO_JPEG_H - -/*! \file cairo_jpg.h - * This file contains all prototypes for the Cairo-JPEG functions implemented - * in cairo_jpg.c. See there for the function documentation. - * - * @author Bernhard R. Fischer, 4096R/8E24F29D <bf@abenteuerland.at> - * @version 2018/12/11 - * @license LGPL3 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include <cairo.h> -#include <stddef.h> - -#ifdef USE_CAIRO_READ_FUNC_LEN_T -/*! This is the type for the stream read function. Which must be implemented by - * the user if cairo_image_surface_create_from_jpeg_stream() is used. Please - * note that this prototype is slightly different from cairo_read_func_t which - * is used by cairo_image_surface_create_from_png_stream(). - * This new prototype is defined because the original prototype - * cairo_read_func_t does not allow to detect truncated reads. This issue was - * discussed on the cairographics mailinglist, see - * https://lists.cairographics.org/archives/cairo/2016-March/027298.html - * @param closure This parameter is directly passed through by - * cairo_image_surface_create_from_jpeg_stream(). - * @param data Pointer to data buffer which will receive the data. - * @param length Size of the data buffer in bytes. - * @return This function must return the actually length that was read into the - * buffer. This may actually be less than length which indicates an EOF. In - * case of any fatal unrecoverable error on the input stream -1 shall be - * returned. - */ -typedef ssize_t (*cairo_read_func_len_t)(void *closure, unsigned char *data, unsigned int length); -#endif - -cairo_status_t cairo_image_surface_write_to_jpeg_mem(cairo_surface_t *sfc, unsigned char **data, - size_t *len, int quality); -cairo_status_t cairo_image_surface_write_to_jpeg_stream(cairo_surface_t *sfc, - cairo_write_func_t write_func, - void *closure, int quality); -cairo_status_t cairo_image_surface_write_to_jpeg(cairo_surface_t *sfc, const char *filename, - int quality); -cairo_surface_t *cairo_image_surface_create_from_jpeg_mem(void *data, size_t len); -#ifdef USE_CAIRO_READ_FUNC_LEN_T -cairo_surface_t *cairo_image_surface_create_from_jpeg_stream(cairo_read_func_len_t read_func, - void *closure); -#else -cairo_surface_t *cairo_image_surface_create_from_jpeg_stream(cairo_read_func_t read_func, - void *closure); -#endif -cairo_surface_t *cairo_image_surface_create_from_jpeg(const char *filename); - -#endif diff --git a/zathura-note/note.c b/zathura-note/note.c index 282e17b..8666af8 100644 --- a/zathura-note/note.c +++ b/zathura-note/note.c @@ -1,13 +1,14 @@ // Copyright (c) 2021 Marvin Borner // WTFPL License (only for note.*) -#include "cairo_jpg.h" #include "plugin.h" #include <plist/plist.h> #include <stdio.h> #include <zip.h> +#include <jpeglib.h> + // Data struct for entire document typedef struct { zip_t *zip; @@ -27,6 +28,10 @@ typedef struct { #define SESSION_OBJECTS_GENERAL_INFO 1 #define SESSION_OBJECTS_LAYOUT_INFO 2 +/** + * Zip wrappers/utilities + */ + static void zip_load(zip_t *zip, const char *root_name, const char *path, void **buf, size_t *length) { @@ -54,6 +59,10 @@ static void zip_load(zip_t *zip, const char *root_name, const char *path, void * } } +/** + * Plist wrappers/utilities + */ + // For debugging/reverse engineering #define INDENT 4 static void plist_dump(plist_t plist, int depth) @@ -329,6 +338,98 @@ static float plist_page_width(plist_t objects) return val; } +/** + * Cairo wrappers/utilities + */ + +typedef struct { + char *data; + size_t length; +} cairo_read_closure; + +// Cairo is weird. Why can't we just pass a data buffer directly? +static cairo_status_t cairo_read(void *data, unsigned char *buf, unsigned int length) +{ + cairo_read_closure *closure = data; + + if (length > closure->length) + return CAIRO_STATUS_READ_ERROR; + + memcpy(buf, closure->data, length); + + closure->length -= length; + closure->data += length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t *cairo_surface_scale(cairo_surface_t *surface, float width, float height) +{ + int unscaled_width = cairo_image_surface_get_width(surface); + int unscaled_height = cairo_image_surface_get_height(surface); + cairo_surface_t *result = cairo_surface_create_similar( + surface, cairo_surface_get_content(surface), width, height); + cairo_t *cairo = cairo_create(result); + cairo_scale(cairo, width / (float)unscaled_width, height / (float)unscaled_height); + cairo_set_source_surface(cairo, surface, 0, 0); + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + cairo_paint(cairo); + cairo_destroy(cairo); + return result; +} + +cairo_surface_t *cairo_image_surface_create_from_jpeg_mem(void *data, size_t len) +{ + struct jpeg_decompress_struct jpeg; + struct jpeg_error_mgr jpeg_err; + jpeg.err = jpeg_std_error(&jpeg_err); + jpeg_create_decompress(&jpeg); + jpeg_mem_src(&jpeg, data, len); + jpeg_read_header(&jpeg, TRUE); + +#ifdef LIBJPEG_TURBO_VERSION +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + jpeg.out_color_space = JCS_EXT_BGRA; +#else + jpeg.out_color_space = JCS_EXT_ARGB; +#endif +#else + jpeg.out_color_space = JCS_RGB; +#endif + + jpeg_start_decompress(&jpeg); + + cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, jpeg.output_width, + jpeg.output_height); + if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { + jpeg_destroy_decompress(&jpeg); + return surface; + } + + while (jpeg.output_scanline < jpeg.output_height) { + unsigned char *row_address = + cairo_image_surface_get_data(surface) + + (jpeg.output_scanline * cairo_image_surface_get_stride(surface)); + JSAMPROW row_pointer = row_address; + jpeg_read_scanlines(&jpeg, &row_pointer, 1); +#ifndef LIBJPEG_TURBO_VERSION + pix_conv(row_address, 4, row_address, 3, jpeg.output_width); +#endif + } + + cairo_surface_mark_dirty(surface); + jpeg_finish_decompress(&jpeg); + jpeg_destroy_decompress(&jpeg); + + cairo_surface_set_mime_data(surface, CAIRO_MIME_TYPE_JPEG, data, len, free, data); + + return surface; +} + +/** + * Main zathura plugin implementations + */ + GIRARA_HIDDEN zathura_error_t note_document_open(zathura_document_t *document) { zathura_error_t error = ZATHURA_ERROR_OK; @@ -434,42 +535,6 @@ GIRARA_HIDDEN zathura_error_t note_page_clear(zathura_page_t *page, void *data) return ZATHURA_ERROR_OK; } -typedef struct { - char *data; - size_t length; -} cairo_read_closure; - -// Cairo is weird. Why can't we just pass a data buffer directly (like with cairo_jpg)?! -static cairo_status_t cairo_read(void *data, unsigned char *buf, unsigned int length) -{ - cairo_read_closure *closure = data; - - if (length > closure->length) - return CAIRO_STATUS_READ_ERROR; - - memcpy(buf, closure->data, length); - - closure->length -= length; - closure->data += length; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_surface_t *cairo_surface_scale(cairo_surface_t *surface, float width, float height) -{ - int unscaled_width = cairo_image_surface_get_width(surface); - int unscaled_height = cairo_image_surface_get_height(surface); - cairo_surface_t *result = cairo_surface_create_similar( - surface, cairo_surface_get_content(surface), width, height); - cairo_t *cairo = cairo_create(result); - cairo_scale(cairo, width / (float)unscaled_width, height / (float)unscaled_height); - cairo_set_source_surface(cairo, surface, 0, 0); - cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - cairo_paint(cairo); - cairo_destroy(cairo); - return result; -} - static void note_page_render_image_object(note_page_t *page, plist_t objects, int index) { char missing = 0; |