c printing more pages with cairo and cups - c

I need, in C in under a linux environment, to print documents with an image as a background.
I found an example which use Cairo vector graphics library to author a PostScript, and then sends it that off to CUPS for printing.
I modified the initial source code to integrate it with a background image (background-img.png) .
The text to be printed is more than one page, I would like to know how can I print on multiple pages keeping the same image as background and changing only the foreground text ?
How can I resize the background image to match the size of an A4 page ?
Here is the code used as a starting point :
// compile with:
// gcc -Wall -o cairo_print_png cairo_print_png.c `pkg-config --cflags --libs cairo` `cups-config --cflags --libs`
#include <stdio.h>
#include <cairo.h>
#include <cairo-ps.h>
#include <cups/cups.h>
// A4 width, height in points, from GhostView manual:
// http://www.gnu.org/software/gv/manual/html_node/Paper-Keywords-and-paper-size-in-points.html
#define WIDTH 595
#define HEIGHT 842
int main(int argc, char** argv) {
int widthPng, heightPng;
if (argc!= 2){
fprintf (stderr, "usage: %s word\n", argv[0]);
return 1;
}
// setup
char* tmpfilename = tempnam(NULL,NULL);
cairo_surface_t* surface = cairo_ps_surface_create(tmpfilename,
WIDTH,
HEIGHT);
cairo_t *context = cairo_create(surface);
// draw some text
cairo_select_font_face(context,
"mono",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(context, 30);
cairo_move_to(context, WIDTH/2, HEIGHT/2);
cairo_show_text(context, argv[1]); // the text we got as a parameter
// draw a dotted box
const double pattern[] = {15.0, 10.0};
cairo_set_dash(context, pattern, 2, 0);
cairo_set_line_width(context, 5);
cairo_rectangle(context, WIDTH*0.33, HEIGHT*0.33, WIDTH*0.5, WIDTH*0.5);
cairo_stroke(context);
cairo_surface_t* surface_png = cairo_image_surface_create_from_png("background-img.png");
if (surface_png == NULL || cairo_surface_status (surface_png)) {
printf("***** load error *****\n");
}
widthPng = cairo_image_surface_get_width(surface_png);
heightPng = cairo_image_surface_get_height(surface_png);
cairo_surface_set_device_scale (surface_png,
widthPng/WIDTH*1.33,
heightPng/HEIGHT*1.29);
cairo_set_operator(context, CAIRO_OPERATOR_DEST_OVER);
cairo_set_source_surface(context, surface_png, 0, 0);
cairo_paint(context);
/*** second page ***/
{
cairo_t *cr;
cr = cairo_create (surface);
/* Duplicate the last frame onto another page. (This is just a
* way to sneak cairo_copy_page into the test).
*/
cairo_show_page (cr);
//draw text on second page
cairo_select_font_face(cr,
"mono",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 30);
cairo_move_to(cr, WIDTH/2, HEIGHT/2);
cairo_show_text(cr, "text over second page");
cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER);
cairo_set_source_surface(cr, surface_png, 0, 0);
cairo_paint(cr);
cairo_destroy (cr);
}
// finish up
cairo_show_page(context);
cairo_destroy(context);
cairo_surface_flush(surface);
cairo_surface_destroy(surface);
cairo_surface_flush(surface_png);
cairo_surface_destroy(surface_png);
// print
cupsPrintFile("Cups-PDF", tmpfilename, "cairo PS", 0, NULL);
unlink(tmpfilename);
return 0;
}
Update :
I added the text enclosed in "second page". I can create another page, but I can't resize the background image.
How can I resize the image to fit the A4 size?
Update
I used cairo_surface_set_device_scale to resize the image, and the mono font to use a fixed width font
Update
The problem now is that the code works with cairo-1.15.12-4 while if I try to compile with cairo-1.8.8-3.1 I get undefined reference to cairo_surface_set_device_scale'.
What can I solve the problem? At the moment I cannot update the libraries using rpm as I would have to update the whole operating system.
Can I somehow replace the cairo_surface_set_device_scale with functions that perform the same task?
Update:
In the Cairo Mailing Lists I found the following function that does the job :
cairo_surface_t *scale_to_half(cairo_surface_t *s, int orig_width, int
orig_height, double x_scale, double y_scale)
{
cairo_surface_t *result = cairo_surface_create_similar(s,
cairo_surface_get_content(s), orig_width*x_scale, orig_height*y_scale);
cairo_t *cr = cairo_create(result);
cairo_scale(cr, x_scale, y_scale);
cairo_set_source_surface(cr, s, 0, 0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
cairo_destroy(cr);
return result;
}
but the quality is very low compared to cairo_surface_set_device_scale.

Related

How do I properly tell SDL to interpret the alpha channel correctly from a pixel map?

I'm not able to get the alpha channel to work properly in SDL using a pixel map. Essentially it seems to get fairly transparent at low values, but as the alpha values approach 0, the pixels become black.
I've tried setting the blend mode for both the texture and the renderer, and searched around for other possible solutions. So far the documentation hasn't helped me figure out a solution. So here is a minimal example I made:
#include "SDL.h"
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
unsigned int* PixelMap;
SDL_Window* Window;
SDL_Renderer* Renderer;
SDL_Texture* Texture;
int
SDL_main(int argc, char* args[])
{
PixelMap = (unsigned int*)malloc(SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(*PixelMap));
SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN, &Window, &Renderer);
Texture = SDL_CreateTexture(Renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT);
SDL_SetTextureBlendMode(Texture, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawBlendMode(Renderer, SDL_BLENDMODE_BLEND);
while(1)
{
for(int I = 0; I < SCREEN_WIDTH * SCREEN_HEIGHT; ++I)
{
PixelMap[I] = 0xFFFCCFFF;
}
for(int I = SCREEN_WIDTH/2 - 100; I < SCREEN_WIDTH/2 + 100; ++I)
{
PixelMap[I + (SCREEN_WIDTH * SCREEN_HEIGHT/2)] = 0x00000000;
}
SDL_UpdateTexture(Texture, 0, PixelMap, SCREEN_WIDTH * sizeof(*PixelMap));
SDL_RenderClear(Renderer);
SDL_RenderCopy(Renderer, Texture, 0, 0);
SDL_RenderPresent(Renderer);
}
}
This isn't the exact code I'm using, but it seems to produce either the same or a similar issue. Here I would expect the line drawn to be transparent since I'm assigning 0 to the location, the blend mode has been set, and the pixel format is set to RGBA8888. But I'm seeing a black line instead.
What am I missing?
Thanks for reading!
EDIT: Oh, duh! I'm overwriting the data, so it has nothing to blend with-- I guess the default background color is black, so it's blending with that. So the solution would be to render the background first, then add the changed pixels on top of that.
I'll leave this up in case it helps anyone else in the future.

I need help about SDL 2

I have a problem when I run my prog : "prog.exe just stopped working".
#include <SDL.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
SDL_Surface *screen; // even with SDL2, we can still bring ancient code back
SDL_Window *window;
SDL_Surface *image;
SDL_Init(SDL_INIT_VIDEO); // init video
// create the window like normal
window = SDL_CreateWindow("SDL2 Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
// but instead of creating a renderer, we can draw directly to the screen
screen = SDL_GetWindowSurface(window);
// let's just show some classic code for reference
SDL_FillRect(image, NULL, SDL_MapRGB(image->format, 0, 255, 0));
SDL_BlitSurface(image, NULL, screen, NULL); // blit it to the screen
SDL_FreeSurface(image);
// this works just like SDL_Flip() in SDL 1.2
SDL_UpdateWindowSurface(window);
// show image for 2 seconds
SDL_Delay(2000);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
// gcc src/main.c -o bin/prog -I include -L lib -lmingw32 -lSDL2main -lSDL2
As Eugene Sh. pointed out your surface isn't initialized
You need to create the surface someway, either by loading an IMG or using SDL_CreateRGBSurface. Add this before calling SDL_FillRect and now your code shows a green screen.
image = SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0);

SDL TTF - Line wrapping & changing height of wrapped lines?

I've been programming a small text adventure game using SDL2 recently and have come across an issue with line wrapping. I am using TTF_RenderText_Blended_Wrapped() to render my strings, and this gives me some nicely wrapped lines. But The line height is an issue, the lines seem squished together, and letters like 'jqg' overlap with letters like 'tli'.
Does anyone know if there is a way to change the line height? TTF_RenderText_Blended_Wrapped() still isn't even in the documentation for SDL_ttf. Should I just write my own text wrapping function?
The font size is 16pt, styling is TTF_STYLE_BOLD, and the font can be found here. The code below should reproduce the error, there is almost no error checking though, use at your own risk. Here is the output of the code:
#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
int main(int argc, char *argv[]) {
SDL_Window *gui;
SDL_Surface *screen, *text;
SDL_Event ev;
TTF_Font *font;
int running = 1;
const char *SAMPLETEXT = "This is an example of my problem, for most lines it works fine, albeit it looks a bit tight. But for any letters that \"hang\" below the line, there is a chance of overlapping with the letters below. This isn\'t the end of the world, but I think it makes for rather cluttered text.\n\nNotice the p and k on line 1/2, and the g/t on 2/3 and 3/4.";
// init SDL/TTF
SDL_Init(SDL_INIT_EVERYTHING);
TTF_Init();
// Open and set up font
font = TTF_OpenFont("Anonymous.ttf", 16);
if(font == NULL) {
fprintf(stderr, "Error: font could not be opened.\n");
return 0;
}
TTF_SetFontStyle(font, TTF_STYLE_BOLD);
// Create GUI
gui = SDL_CreateWindow("Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
640, 480, SDL_WINDOW_SHOWN);
// Grab GUI surface
screen = SDL_GetWindowSurface(gui);
// Clear screen black
SDL_FillRect(screen, NULL, 0);
// Draw some text to screen
SDL_Color color = {0xff, 0xff, 0xff, 0xff};
text = TTF_RenderText_Blended_Wrapped(font, SAMPLETEXT, color, screen->w);
SDL_BlitSurface(text, NULL, screen, NULL);
while(running) { // Main loop
while(SDL_PollEvent(&ev)) {
switch(ev.type){
case SDL_QUIT:
running = 0;
break;
}
}
SDL_UpdateWindowSurface(gui); // Refresh window
SDL_Delay(20); // Delay loop
}
// Destroy resources and quit
TTF_CloseFont(font);
TTF_Quit();
SDL_FreeSurface(text);
SDL_DestroyWindow(gui);
SDL_Quit();
return 0;
}
The easiest solution is to find a font that doesn't have that issue. The FreeMono font has more spacing:
From looking at the source code for TTF_RenderUTF8_Blended_Wrapped, which is called by TTF_RenderText_Blended_Wrapped, there is no configurable way to set the spacing between the lines. See const int lineSpace = 2; on line 1893.
However, even though lineSpace is set to 2, it is not being used when computing the address of each pixel to render. This is effectively setting the line spacing to 0. I reported this as a bug in the SDL_ttf library: https://bugzilla.libsdl.org/show_bug.cgi?id=3679
I was able to fix the issue in SDL_ttf 2.0.14 with the following change:
--- a/SDL_ttf.c Fri Jan 27 17:54:34 2017 -0800
+++ b/SDL_ttf.c Thu Jun 22 16:54:38 2017 -0700
## -1996,7 +1996,7 ##
return(NULL);
}
- rowSize = textbuf->pitch/4 * height;
+ rowSize = textbuf->pitch/4 * (height + lineSpace);
/* Adding bound checking to avoid all kinds of memory corruption errors
that may occur. */
With the above patch applied, your example program shows the correct line spacing with the Anonymous font:

how to detect circle in image with openCV?

I want to detect circles in the image.this is the code i tried for detecting number of circles in this image:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <stdio.h>
/** #function main */
int main(int argc, char** argv)
{
//load image from directory
IplImage* gray = cvLoadImage("comb.png",0);
// IplImage* gray = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
CvMemStorage* storage = cvCreateMemStorage(0);
//covert to grayscale
cvCvtColor(gray, gray, CV_RGB2GRAY);
// This is done so as to prevent a lot of false circles from being detected
cvSmooth(gray, gray, CV_GAUSSIAN, 7, 7);
IplImage* canny = cvCreateImage(cvGetSize(gray),IPL_DEPTH_8U,1);
//IplImage* rgbcanny = cvCreateImage(cvGetSize(gray),IPL_DEPTH_8U,3);
cvCanny(gray, canny, 10, 20, 3);
//detect circles
CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 1, gray->width/10);
if(circles->total>0)
printf("circles found: %d\n",circles->total);
else
printf("circles not found");
return 0;
}
but the following error occurs :-
OpenCV Error: Assertion failed (dst.data == dst0.data) in cvCvtColor, file /OpenCV/OpenCV-2.4.3/modules/imgproc/src/color.cpp, line 3811
terminate called after throwing an instance of 'cv::Exception'
what(): /OpenCV/OpenCV-2.4.3/modules/imgproc/src/color.cpp:3811: error: (-215) dst.data == dst0.data in function cvCvtColor
please tell me what is wrong with the program.i am using ubuntu linux.
You open gray as a gray image (cvLoadImage("comb.png",0), with the "0" meaning "grayscale"). Then you try to convert this gray frame from RGB to Gray, and of course this cannot work since your image is already gray.
Simply try to comment out the following line:
//covert to grayscale
cvCvtColor(gray, gray, CV_RGB2GRAY);
By the way, you should use
IplImage* gray = cvLoadImage("comb.png", CV_LOAD_IMAGE_GRAYSCALE);
instead of
IplImage* gray = cvLoadImage("comb.png", 0);
That is clearer.

How to draw directly on the desktop?

I'm wondering how to draw directly on the root window in an X11 environment with Cairo (in C), in order to make widgets. I've copied some parts of the code of tint2, but it's quite enormous, and the only result I have is not satisfying.
I would be pleased to have a complete working sample code, or at least some tips or little programs to study.
Thank you guys !
The "bottom" window is the root window. The problem is that in some desktop environments we have windows on top of the root window, so if you change the root window, you won't see your changes: you need to change the window that's on the top.
This program does what you ask for: draw on the root window. To test it, I suggest you to:
ctrl+alt+f1
login as root
stop your desktop environment ("/etc/init.d/gdm stop", "/etc/init.d/kdm stop" or whatever is needed in your distro)
X -noreset -retro &
DISPLAY=:0.0 xterm &
DISPLAY=:0.0 metacity &
Then, go back to X (ctrl+alt+f7 or maybe f8) and run the program.
If you want to draw on Nautilus' top window, you will need to find out its window ID and then use it as the "w" variable. The "xwininfo" command might help you testing...
#include <assert.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <cairo.h>
#include <cairo-xlib.h>
int width, height;
void draw(cairo_t *cr) {
int quarter_w = width / 4;
int quarter_h = height / 4;
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
cairo_rectangle(cr, quarter_w, quarter_h, quarter_w * 2, quarter_h * 2);
cairo_fill(cr);
}
int main() {
Display *d = XOpenDisplay(NULL);
assert(d);
int s = DefaultScreen(d);
Window w = RootWindow(d, s);
width = DisplayWidth(d, s);
height = DisplayHeight(d, s);
cairo_surface_t *surf = cairo_xlib_surface_create(d, w,
DefaultVisual(d, s),
width, height);
cairo_t *cr = cairo_create(surf);
XSelectInput(d, w, ExposureMask);
draw(cr);
XEvent ev;
while (1) {
XNextEvent(d, &ev);
printf("Event!\n");
if (ev.type == Expose) {
draw(cr);
}
}
cairo_destroy(cr);
cairo_surface_destroy(surf);
XCloseDisplay(d);
return 0;
}

Resources