Skip to content

Implement predefined and custom paper sizes - #87

Merged
jubalh merged 4 commits into
jubalh:masterfrom
aura-lsprog-86:feature-papersize2
Apr 13, 2026
Merged

jubalh merged 4 commits into
jubalh:masterfrom
aura-lsprog-86:feature-papersize2

Conversation

@aura-lsprog-86

@aura-lsprog-86 aura-lsprog-86 commented Apr 11, 2026 •

Copy link
Copy Markdown
Contributor

From the discussion in #86, I went ahead and implemented scaffolding to detect predefined and custom page sizes, especially taking into account #86 (comment). 😄

Now, option -S accepts any of the following options:

  • One of the predefined sizes: a4 (default), a5, letter, legal, invoice, and oficio.
  • A custom size, described with the format WIDTHxHEIGHT[unit].
    • unit is optional, and defaults to cm if not given.
    • unit accepts one of in, cm, or pt.
    • If a negative size is given, the program forces it to be positive.

Then, when parsing the options, the program basically follows this parsing path:

  1. Try to get a predefined size, taking the user input as name.
  2. If no match is found, try to parse the input as a custom size. (I use sscanf to aid with this.)
  3. If the input cannot be parsed, nothing is done (the help page is shown and EXIT_FAILURE is returned).

Even though implementing predefined and custom page sizes was not a too difficult task, the behavior of PDF rendering when multiple sudokus are requested is another matter. 😬

The previous implementation hardcoded 4 sudokus per page, and used cairo_scale and cairo_translate to reuse the grid drawing function.

Now, it was necessary to get the current transformation matrix and map user units to device units, or points (each sudoku is composed of squares that measure 50 user units, and so each sudoku is 450x450 user units). To determine how many sudokus fit per axis, the following calculations were done.

// First sudoku uses initial margin as padding.
double fsx = SUDOKU_SPACE(double, di_initial_pad.x);
double fsy = SUDOKU_SPACE(double, di_initial_pad.y);

cairo_matrix_transform_distance(&ctm, &fsx, &fsy);

// Other sudokus use uniform intra-spacing.
double sx = SUDOKU_SPACE(double, di_intra_pad.x);
double sy = SUDOKU_SPACE(double, di_intra_pad.y);

cairo_matrix_transform_distance(&ctm, &sx, &sy);

// No. sudokus = ((Page length - first sudoku length) / other sudokus length) + 1
// The +1 accounts for the first sudoku that was subtracted from the page length.

double fx = ((dd_ps.width - fsx) / sx) + 1;
double fy = ((dd_ps.height - fsy) / sy) + 1;

The code above basically means:

  • The first sudoku on the row/column has its own left/top margins, which take into account a predefined intraspacing, and the overall document margins.
  • For any other sudokus, the top and left margins only take into account the predefined intraspacing.

Then, each sudoku is printed in its right place, by calculating the user unit coordinates where Cairo should start drawing each of them, as seen in the code below:

int no_sudokus_per_page = d_fit.x * d_fit.y;

for (int i=0; i < sudokus_count; i++)
{
	int cur_sudoku_col = (i % no_sudokus_per_page) % d_fit.x;
	int cur_sudoku_row = (i % no_sudokus_per_page) / d_fit.x;

	DUPLE_INT start = {
		di_initial_pad.x + (cur_sudoku_col * SUDOKU_SPACE(int, di_intra_pad.x)),
		di_initial_pad.y + (cur_sudoku_row * SUDOKU_SPACE(int, di_intra_pad.y))
	};

	cairo_translate(cr, start.x, start.y);

	char* stream;
	stream = generate_puzzle(difficulty);

#ifdef DEBUG
	printf("%d: %s\n", i, stream);
#endif // DEBUG

	draw_grid(stream, cr);

	cairo_translate(cr, -start.x, -start.y);

	if ((i + 1) < sudokus_count && (i + 1) % no_sudokus_per_page == 0) {
		// Show current page
		cairo_show_page(cr);
		prepare_new_page(cr, difficulty, false);
	}

	free(stream);
}

Overall, I'm quite satisfied by the results achieved. 😌

However, I know my code can be improved. For example, right now there is no upper-limit capping in either -n or -S, so a user can issue something like:

./src/nudoku -p riddle.pdf -n 8375 -S 1621x897in

Therefore, any comments or suggestions are welcome to make this better. 👍🏻

Thanks! 🚀

@jubalh

jubalh commented Apr 12, 2026

Copy link
Copy Markdown
Owner

Please use git rebase to clean up the git history first. I can see several commits with the same description. In case you fix something that you inroduced in this branch just squash those commits into one.

@jubalh jubalh left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment

@aura-lsprog-86
aura-lsprog-86 force-pushed the feature-papersize2 branch 2 times, most recently from 7e3b3e8 to 7061cd9 Compare April 12, 2026 18:57
@aura-lsprog-86
aura-lsprog-86 requested a review from jubalh April 12, 2026 18:58
@aura-lsprog-86
aura-lsprog-86 force-pushed the feature-papersize2 branch 2 times, most recently from ea4e8cb to 9afe348 Compare April 12, 2026 19:06
Comment thread src/utils.c Outdated
Comment thread src/utils.c Outdated
Comment thread src/utils.c Outdated
Includes sizes "a4", "a5", "letter", "legal", "invoice", and "oficio".
* Add page header in every page.
* Calculate number of sudokus that fit inside a given page size.
* Do not allow PDF generation if no sudokus can fit in a page.
* Ensure Cairo objects are destroyed upon PDF failure
Include "outp.c" in list of translatable files.
@jubalh
jubalh merged commit 5e94821 into jubalh:master Apr 13, 2026
2 checks passed
@jubalh

jubalh commented Apr 13, 2026

Copy link
Copy Markdown
Owner

Thanks for the feature!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants