1
0
mirror of https://github.com/DanBloomberg/leptonica.git synced 2026-10-23 15:28:56 +03:00

New program for selective orthogonal rotation of images in a pdf.

* Give 3 modes for specifying rotations
* Allow scaling of the images
This commit is contained in:
danblooomberg
2026-04-18 16:52:20 -07:00
parent dc88c95380
commit bb244a3176
8 changed files with 427 additions and 4 deletions

View File

@@ -298,6 +298,7 @@ add_prog_target(renderfonts renderfonts.c)
add_prog_target(replacebytes replacebytes.c)
add_prog_target(rotatefastalt rotatefastalt.c)
add_prog_target(rotate_it rotate_it.c)
add_prog_target(rotateorthpdf rotateorthpdf.c)
add_prog_target(rotateorthtest1 rotateorthtest1.c)
add_prog_target(rotateorth_reg rotateorth_reg.c)
add_prog_target(rotatetest1 rotatetest1.c)

View File

@@ -112,7 +112,7 @@ OTHER_PROGS = adaptmap_dark \
reducetest removecmap \
renderfonts replacebytes \
rotatefastalt rotate_it \
rotateorthtest1 rotatetest1 \
rotateorthpdf rotateorthtest1 rotatetest1 \
runlengthtest scaleandtile scaleimages \
scale_it scaletest1 scaletest2 \
seedfilltest settest sharptest \

View File

@@ -252,7 +252,7 @@ SRC = adaptmap_reg.c adaptnorm_reg.c affine_reg.c \
recogtest4.c recogtest5.c recogtest6.c recogtest7.c \
reducetest.c removecmap.c \
renderfonts.c replacebytes.c \
rotatefastalt.c rotate_it.c \
rotatefastalt.c rotate_it.c rotateorthpdf.c \
rotateorthtest1.c rotatetest1.c \
runlengthtest.c scaleandtile.c scaleimages.c \
scale_it.c scaletest1.c scaletest2.c \
@@ -1146,6 +1146,9 @@ rotatefastalt: rotatefastalt.o $(LEPTLIB)
rotate_it: rotate_it.o $(LEPTLIB)
$(CC) -o rotate_it rotate_it.o $(ALL_LIBS) $(EXTRALIBS)
rotateorthpdf: rotateorthpdf.o $(LEPTLIB)
$(CC) -o rotateorthpdf rotateorthpdf.o $(ALL_LIBS) $(EXTRALIBS)
rotateorthtest1: rotateorthtest1.o $(LEPTLIB)
$(CC) -o rotateorthtest1 rotateorthtest1.o $(ALL_LIBS) $(EXTRALIBS)

157
prog/rotateorthpdf.c Normal file
View File

@@ -0,0 +1,157 @@
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials
- provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*====================================================================*/
/*
* rotateorthpdf.c
*
* This program rotates individual page images in a pdf of images.
* It takes a rotation string that has three modes:
* (1) a list of integers in [0,1,2,3] for 90 degree cw rotations; only
* need to list up to last non-zero rotation
* (2) a 4, followed by one integer, to rotate all images by the same amount
* (3) a 5, followed by parenthesized comma-separated pairs of numbers:
* (page, rotation).
*
* Syntax:
* rotateorthpdf filein imres rotstring scalefactor quality
* title fileout
*
* The %imres is the desired resolution of the rasterization from the
* pdf page to a page image. Two choices are allowed: 150 and 300 ppi.
* Use 0 for default (150 ppi). The actual resolution used by the
* renderer depends on the page image size and is computed internally.
* We limit the maximum resolution to 300 ppi because these images are
* RGB uncompressed and are large: 6.3 MB for 150 ppi and 25 MB for 300 ppi.
*
* The %rotstring flag determines which, if any, images are rotated cw by
* multiples of 90 degrees. There are 3 modes, and here are examples:
* Mode 1: "00201". The third image is rotated by 180 degrees and
* the fifth by 90 degrees. All others are not rotated.
* Mode 2: "41". All images are rotated 90 degrees cw.
* Mode 3: "5(12,3)(19,1)". Image 12 is rotated cw by 270 degrees and image
* 19 is rotated by 90 degrees. All others are not rotated.
* Note that images are numbered from 0, not from 1.
*
* The %scalefactor is the scaling to be applied to each image. You
* can use any positive value not exceeding 2.0.
*
* The %quality is the jpeg output quality factor for images stored
* with DCT encoding in the pdf. Use 0 for the default value (50),
* which is satisfactory for many purposes. Use 75 for standard
* jpeq quality; 85-95 is very high quality. Allowed values are
* between 25 and 95.
*
* The %title is the title given to the pdf. Use %title == "none"
* to omit the title.
*
* The pdf output is written to %fileout. It is advisable (but not
* required) to have a '.pdf' extension.
*
* As the first step in processing, images are saved in the directory
* /tmp/lept/renderpdf/, as RGB in ppm format, and at the resolution
* specified by %imres, either 150 or 300 ppi. Page-sized images will
* rendered at 150 ppi will be about 6MB; at 300 ppi they will be 25 MB.
*
* We use pdftoppm to render the images at (typically) 150 pixels/inch.
* The renderer uses the mediaboxes to decide how big to make the
* images. If those boxes have values that are too large, the
* intermediate ppm images can be very large. To prevent that,
* we compute the resolution to input to pdftoppm that results in
* page images at the requested resolution.
*
* N.B. This requires running pdftoppm from the Poppler package
* of pdf utilities For non-unix systems, this requires
* installation of the cygwin Poppler package:
* https://cygwin.com/cgi-bin2/package-cat.cgi?file=x86/poppler/
* poppler-0.26.5-1
*/
#ifdef HAVE_CONFIG_H
#include <config_auto.h>
#endif /* HAVE_CONFIG_H */
#include "allheaders.h"
l_int32 main(int argc,
char **argv)
{
char buf[256];
char *rotstring, *filein, *title, *fileout;
l_int32 imres, render_res, quality;
l_float32 scalefactor;
SARRAY *safiles;
if (argc != 8)
return ERROR_INT(
"Syntax: rotateorthpdf filein imres rotstring"
" scalefactor quality title fileout", __func__, 1);
filein = argv[1];
imres = atoi(argv[2]);
rotstring = argv[3];
scalefactor = atof(argv[4]);
quality = atoi(argv[5]); /* jpeg quality */
title = argv[6];
fileout = argv[7];
if (imres <= 0) imres = 150; /* default value */
if (imres != 150 && imres != 300) {
L_WARNING("imres = %d must be 150 or 300; setting to 150\n",
__func__, imres);
imres = 150;
}
if (scalefactor <= 0.0) scalefactor = 1.0;
if (scalefactor > 2.0) {
L_WARNING("scalefactor %f too big; setting to 2.0\n", __func__,
scalefactor);
scalefactor = 2.0;
}
if (quality <= 0) quality = 50; /* default value */
if (quality < 25) {
L_WARNING("quality = %d is too low; setting to 25\n",
__func__, quality);
quality = 25;
}
if (quality > 95) {
L_WARNING("quality = %d is too high; setting to 95\n",
__func__, quality);
quality = 95;
}
setLeptDebugOK(1);
/* Render all images from the pdf file */
if (l_pdfRenderFile(filein, imres, &safiles))
return ERROR_INT_1("rendering failed from filein", filein,
__func__, 1);
/* Optionally rotate, then scale and collect all images in memory.
* If n > 100, use pixacomp instead of pixa to store everything
* before generating the pdf. */
lept_stderr("rotating ...\n");
rotateorthFilesToPdf(safiles, rotstring, scalefactor, quality,
title, fileout);
sarrayDestroy(&safiles);
return 0;
}

View File

@@ -1409,6 +1409,7 @@ LEPT_DLL extern BOXA * boxaPruneSortedOnOverlap ( BOXA *boxas, l_float32 maxover
LEPT_DLL extern l_ok compressFilesToPdf ( SARRAY *sa, l_int32 onebit, l_int32 savecolor, l_float32 scalefactor, l_int32 quality, const char *title, const char *fileout );
LEPT_DLL extern l_ok cropFilesToPdf ( SARRAY *sa, l_int32 lr_clear, l_int32 tb_clear, l_int32 edgeclean, l_int32 lr_border, l_int32 tb_border, l_float32 maxwiden, l_int32 printwiden, const char *title, const char *fileout );
LEPT_DLL extern l_ok cleanTo1bppFilesToPdf ( SARRAY *sa, l_int32 res, l_int32 contrast, l_int32 rotation, l_int32 opensize, const char *title, const char *fileout );
LEPT_DLL extern l_ok rotateorthFilesToPdf ( SARRAY *sa, const char *rotstring, l_float32 scalefactor, l_int32 quality, const char *title, const char *fileout );
LEPT_DLL extern l_ok convertFilesToPdf ( const char *dirname, const char *substr, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout );
LEPT_DLL extern l_ok saConvertFilesToPdf ( SARRAY *sa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout );
LEPT_DLL extern l_ok saConvertFilesToPdfData ( SARRAY *sa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, l_uint8 **pdata, size_t *pnbytes );

View File

@@ -69,6 +69,12 @@
*
* Cleanup and binarization of images for prog/cleanpdf
* l_int32 cleanTo1bppFilesToPdf()
*
* Orthogonal rotation of images for prog/rotateorthpdf
* l_int32 rotateorthFilesToPdf()
*
* Static helpers
* NUMA *parseRotationString()
* </pre>
*/
@@ -79,6 +85,10 @@
#include <string.h>
#include "allheaders.h"
static NUMA *parseRotationString(l_int32 n, const char *rotstring);
#define DEBUG_PARSER 0
/* --------------------------------------------*/
#if USE_PDFIO /* defined in environ.h */
@@ -490,6 +500,253 @@ PIXAC *pixac1 = NULL;
return 0;
}
/*---------------------------------------------------------------------*
* Orthogonal rotation of images for prog/rotateorthpdf *
*---------------------------------------------------------------------*/
/*!
* \brief rotateorthFilesToPdf()
*
* \param[in] sa sorted full pathnames of images
* \param[in] rotstring determines which images are to be rotated
* \param[in] scalefactor scaling factor applied to each image; (0.0...2.0]
* \param[in] quality for jpeg: 0 for default (75); otherwise 25 - 95.
* \param[in] title [optional] pdf title; can be null
* \param[in] fileout pdf file of all images
* \return 0 if OK, 1 on error
*
* <pre>
* Notes:
* (1) This function rotates selected images and wraps them in a pdf in
* the order given in the input %sa.
* (2) It does the image processing for prog/rotateorthpdf.c.
* (3) The %rotstring has three modes:
* mode 1: a list of integers in [0,1,2,3] for 90 degree cw rotations;
* only need to list up to last non-zero rotation
* mode 2: a 4, followed by one integer, to rotate all images by the
* same amount
* mode 3: a 5, followed by parenthesized comma-separated pairs
* of numbers: (page, rotation).
* Note that page numbers are 0-based (e.g., the first page is page 0)
* (4) Images in the output pdf are encoded with jpeg (DCT).
* (5) Typically, %scalefactor <= 1.0. It is applied to each image
* before encoding. If you enter a value <= 0.0, it will be set to 1.0.
* The maximum allowed value is 2.0. Using a value < 1.0 results in
* lower resolution in the images in the output pdf.
* (6) Default jpeg %quality is 75; otherwise, quality factors between
* 25 and 95 are enforced.
* </pre>
*/
l_ok
rotateorthFilesToPdf(SARRAY *sa,
const char *rotstring,
l_float32 scalefactor,
l_int32 quality,
const char *title,
const char *fileout)
{
char *fname;
l_int32 n, i, res, maxi, rotval;
l_int32 maxsmallset = 25; /* max num images kept uncompressed in array */
NUMA *na1;
PIX *pixs, *pix1, *pix2, *pix3;
PIXA *pixa1 = NULL;
PIXAC *pixac1 = NULL;
if (!sa)
return ERROR_INT("sa not defined", __func__, 1);
if (!fileout)
return ERROR_INT("fileout not defined", __func__, 1);
if (scalefactor <= 0) scalefactor = 1.0;
if (scalefactor > 2.0) {
L_WARNING("scalefactor %f too big; setting to 2.0\n", __func__,
scalefactor);
scalefactor = 2.0;
}
if (quality <= 0) quality = 75; /* default value */
if (quality < 25) {
L_WARNING("quality %d too low; setting to 25\n", __func__, quality);
quality = 25;
}
if (quality > 95) {
L_WARNING("quality %d too high; setting to 95\n", __func__, quality);
quality = 95;
}
if ((n = sarrayGetCount(sa)) == 0)
return ERROR_INT("sa is empty", __func__, 1);
if ((na1 = parseRotationString(n, rotstring)) == NULL)
return ERROR_INT("invalid rotstring", __func__, 1);
maxi = numaGetCount(na1);
#if DEBUG_PARSER
{
NUMA *na = parseRotationString(17, "5(3,2)##,(2,5);;(7,2)(92)(\
((12,-2)((14,3)(8,1)xy(-7,2)z(23,1)");
numaWrite("/tmp/lept/debug.na", na);
numaDestroy(&na);
}
#endif /* DEBUG_PARSER */
if (n <= maxsmallset)
pixa1 = pixaCreate(n);
else
pixac1 = pixacompCreate(n);
for (i = 0; i < n; i++) {
if (i == 0)
lept_stderr("page: ");
if (n < 12) {
lept_stderr("%d . ", i);
} else {
if (i && (i % 10 == 0))
lept_stderr("%d . ", i);
}
fname = sarrayGetString(sa, i, L_NOCOPY);
pixs = pixRead(fname);
pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
if (i <= maxi) {
numaGetIValue(na1, i, &rotval);
if (rotval == 0)
pix2 = pixClone(pix1);
else
pix2 = pixRotateOrth(pix1, rotval);
} else {
pix2 = pixClone(pix1);
}
pix3 = pixScale(pix2, scalefactor, scalefactor);
if (n <= maxsmallset) {
pixaAddPix(pixa1, pix3, L_INSERT);
} else {
pixacompAddPix(pixac1, pix3, IFF_DEFAULT);
pixDestroy(&pix3);
}
pixDestroy(&pixs);
pixDestroy(&pix1);
pixDestroy(&pix2);
}
/* Generate the pdf. Infer the input resolution from the first
* image, assuming the longest side is (scalefactor * 11.0) inches.
* Then if the first image in the output is printed, the longest
* side will be (scalefactor * 11.0) inches, regardless of the
* resolution of the printer. The other images, if printed, will
* be sized relative to the first image by the ratio of their
* longest side to the longest side of the first image. */
lept_stderr("\nWrite output to %s\n", fileout);
if (n <= maxsmallset)
pix1 = pixaGetPix(pixa1, 0, L_CLONE);
else
pix1 = pixacompGetPix(pixac1, 0);
pixInferResolution(pix1, scalefactor * 11.0, &res);
pixDestroy(&pix1);
if (strcmp(title, "none") == 0)
title = NULL;
if (n <= maxsmallset) {
pixaConvertToPdf(pixa1, res, 1.0, L_DEFAULT_ENCODE, quality,
title, fileout);
pixaDestroy(&pixa1);
} else {
pixacompConvertToPdf(pixac1, res, 1.0, L_DEFAULT_ENCODE,
quality, title, fileout);
pixacompDestroy(&pixac1);
}
return 0;
}
/*!
* \brief parseRotationString()
*
* \param[in] n number of images
* \param[in] rotstring specifies orthogonal rotations of the images
* \return numa containing the rotation values, or NULL on error
*
* <pre>
* Notes:
* (1) The %rotstring has three modes:
* (a) a list of integers in [0,1,2,3] for 90 degree cw rotations;
* only need to list up to last non-zero rotation
* (b) a 4, followed by one integer, to rotate all images by the
* same amount
* (c) a 5, followed by parenthesized comma-separated pairs
* of numbers: (page, rotation).
* (2) The returned numa has the rotation value for each image; a value
* of 0 means no rotation.
* (3) Examples of input rotstring:
* Mode 1: "22000003113"
* Mode 2: "42"
* Mode 3: "5(3,2)(2,1)(14,3)(8,1)(23,1)" or (with separators)
* "5::(3,2),(2,1)##(14,3);;(8,1)(23,1)"
* Note: (1) arbitrary separators can be put between
* parenthesized pairs without affecting the result
* (2) the page order of parenthesized pairs does not matter
* (4) Invalid pairs in mode 3, where the page index is larger than n-1,
* or the rotation number is invalid, are ignored with a warning.
* (5) This function can be tested by defining DEBUG_PARSER to be 1.
* </pre>
*/
static NUMA *
parseRotationString(l_int32 n,
const char *rotstring)
{
l_int32 ret, i, len, rotval, mode, maxn, loc, ipair, npair, index;
L_DNA *dna;
NUMA *na;
if (!rotstring)
return (NUMA *)ERROR_PTR("rotstring not defined", __func__, NULL);
if ((len = strlen(rotstring)) == 0)
return (NUMA *)ERROR_PTR("rotstring length == 0", __func__, NULL);
rotval = rotstring[0] - 0x30;
if (rotval >= 0 && rotval <= 3)
mode = 1;
else if (rotval == 4)
mode = 2;
else if (rotval == 5)
mode = 3;
else
return (NUMA *)ERROR_PTR("invalid rotstring mode", __func__, NULL);
if ((na = numaMakeConstant(0, n)) == NULL)
return (NUMA *)ERROR_PTR("na not made for n = %d", __func__, NULL);
if (mode == 1) {
maxn = L_MIN(len, n);
for (i = 0; i < maxn; i++)
numaSetValue(na, i, rotstring[i] - 0x30);
} else if (mode == 2) {
for (i = 0; i < n; i++)
numaSetValue(na, i, rotstring[1] - 0x30);
} else { /* mode 3; data can be in any order of image index */
dna = stringFindEachSubstr(rotstring, "(");
npair = l_dnaGetCount(dna);
for (ipair = 0; ipair < npair; ipair++) {
l_dnaGetIValue(dna, ipair, &loc);
if (loc > len - 5) {
L_ERROR("invalid loc value = %d\n", __func__, loc);
continue;
}
ret = sscanf(&rotstring[loc], "(%d,%d)", &index, &rotval);
if (ret != 2) {
L_ERROR("invalid pair for loc = %d\n", __func__, loc);
continue;
}
if (index < 0 || index >= n) {
L_ERROR("invalid index = %d >= nimages = %d\n", __func__,
index, n);
continue;
}
if (rotval < 0 || rotval > 3) {
L_ERROR("invalid rotval = %d at index = %d\n", __func__,
rotval, index);
continue;
}
numaSetValue(na, index, rotval);
}
l_dnaDestroy(&dna);
}
return na;
}
/* --------------------------------------------*/
#endif /* USE_PDFIO */
/* --------------------------------------------*/

1
sw.cpp
View File

@@ -341,6 +341,7 @@ void build(Solution &s)
{"replacebytes", {"replacebytes.c"}},
{"rotatefastalt", {"rotatefastalt.c"}},
{"rotate_it", {"rotate_it.c"}},
{"rotateorthpdf", {"rotateorthpdf.c"}},
{"rotateorthtest1", {"rotateorthtest1.c"}},
{"rotateorth_reg", {"rotateorth_reg.c"}},
{"rotatetest1", {"rotatetest1.c"}},

View File

@@ -92,9 +92,12 @@ They are <i>not</i> a complete listing of the modifications.
1.88.0 Not yet released
* Split out a static data reading part of pnm stream reader.
* Fixed integer overflow in sarray to string conversion.
* Source files changed: bmf.c, pnmio.c, sarray1.c,
stringcode.c, tiffio.c
* New program for selective orthogonal rotation of images in a pdf.
* Source files changed: bmf.c, boxbasic,c, pix1.c, pix2.c,
pnmio.c, pdfapp.c, readbarcode.c,
sarray1.c, stringcode.c, tiffio.c, utils2.c
* Prog files changed: renderfonts.c
* New prog files: rotateorthpdf.c
1.87.0 Dec 24, 2025
* New distribution