Compter le nombre de pages d'un fichier pdf

Soyez le premier à donner votre avis sur cette source.

Snippet vu 6 442 fois - Téléchargée 19 fois

Contenu du snippet

Ce petit code source permet de compter le nombre de pages dans un fichier PDF.
Fonctionne au moins avec les PDF jusqu'au 1.4

les spécifications 1.5 voient entrer la compression de la table XRef... du coup, pour ce simple comptage, le code se voit allongé de deux a trois fois sa taille actuelle, d'après mes premières estimations.

vu que j'ai écrit ce code rapidement, pour un cadre précis, avec des fichiers 1.4...

Source / Exemple :


#if defined(WIN32)
	#define _CRT_SECURE_NO_WARNINGS
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int CountPDFPage(char *Fichier) {
	FILE *pdf;
	unsigned int count = 0;
	int offset;
	int len;
	int i;
	int refCount;
	char *p;
	char buf[1024];
	unsigned int *XRef;
	if ((pdf = fopen(Fichier, "rb")) == NULL) {
		printf("Impossible d'ouvrir le fichier PDF.\n");
		return -1;
	}

	fread(buf, 1, 5, pdf);
	if (strncmp(buf, "%PDF-", 5)) {
		printf("fichier non PDF.\n");
		return -2;
	}

	fseek(pdf, 0, SEEK_END);	
	offset = ftell(pdf)-60;
	fseek(pdf, offset, SEEK_SET);
	len = fread(buf, 1, 60, pdf);
	if (len!=60) {
		printf("fichier PDF corrompu\n");
		return -3;
	}

	buf[60] = 0;
	for(;;) {
		len = strlen(buf);
		if (len==60)
			break;
		else
			buf[len] = 32;
	}

	p = strstr(buf, "startxref");
	if (p==0) {
		printf("Erreur dans l'analyse du pdf. Impossible de trouver le StartXRef\n");
		return -4;			
	}
	
	p+=10;
	while (*p==32 || *p==10 || *p==13) p++;
	offset = atoi(p);
	if (offset<=0) {
		printf("Erreur dans l'analyse du pdf. StartXRef==%d\n", offset);
		return -5;
	}
	fseek(pdf, offset, SEEK_SET);
	
	/* On récupère le nombre d'entree de la XRef */
	fread(buf, 1, 40, pdf);	

	if (strncmp(buf, "xref", 4)) {
		printf("XRef incompatible. Peut être s'agit il d'un fichier PDF 1.5 ou superieur\n");
		return -6;
	}

	p=buf+4;
	while (*p==32 || *p==10 || *p==13) p++;
	while (*p>='0' && *p<='9') p++;
	while (*p==32) p++;

	refCount = atoi(p);
	if (refCount<=0) {
		printf("Erreur dans l'analyse du pdf. XRef==%d\n", refCount);
		return -7;
	}

	/* On va conserver la XRef en memoire. */
	XRef = malloc(sizeof(unsigned int)*--refCount);
	fseek(pdf, offset+32, SEEK_SET);
	for (i=0; i<refCount;) {
		len = fread(buf, 1, 1024, pdf);
		for(p=buf; i<refCount && p<(buf+len); p+=20) {
			XRef[i] = atoi(p);
			i++;
		}
	}

	/* Pour chaque object de la XRef... */
	for (i=0; i<refCount; i++) {
		fseek(pdf, XRef[i], SEEK_SET);	
		fread(buf, 1, 1024, pdf);
		p = strstr(buf, "/Type ");
		if (p) {
			if (strncmp(p, "/Type /Pages", 12))
				break; /* Seuls les /Pages nous interessent... */
			p = strstr(buf, ">>");
			if (p) 

  • p=0; /* Permet de ne pas tenir compte du 'trop-lu' du fichier */
/* Un fichier PDF peut contenir n groupes de pages. */ /* Seul l'element racine nous interesse : celui qui n'a pas de Parent */ if (strstr(buf, "/Parent ") == NULL) { p = strstr(buf, "/Count "); if (p) { count = atoi(p+7); /* Voila notre nombre de pages */ break; } } } } free(XRef); fclose(pdf); return count; } int main(int argc, char *argv[]) { int rc; if (argc!=2) { printf("Compte le nombre de pages d'un fichier PDF dont le chemin est donne en parametre.\n"); return 1; } rc = CountPDFPage(argv[1]); if (rc>0) { printf("%d\n", rc); return 0; } printf("%d\n", rc); return rc; }

Conclusion :


Rien de bien sorcier, mais outil bien utile au quotidien (enfin, dans mon quotidien en tous cas)

A voir également

Ajouter un commentaire

Commentaires

cptpingu
Messages postés
3821
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
28 août 2019
85 -
C'est un outil très intéressant, et qui peut se réveler pratique !

J'ai néanmoins quelques petit soucis:

- Pourrais-tu vérifier le type mime ? Si l'on met un fichier qui n'est pas de type pdf, que se passe-t-il ?
- Chez moi, ton binaire plante (Arch Linux + gcc 4.6).
gdb --args ./a.out monpdf.pdf
Reading symbols from /tmp/a.out...done.
gdb$ r

Program received signal SIGSEGV, Segmentation fault.
0x00000000004009c7 in CountPDFPage (Fichier=0x7fffffffe358 "monpdf.pdf") at toto.c:47
47 { XRef[i] = atoi(p);
gdb$ bt
#0 0x00000000004009c7 in CountPDFPage (Fichier=0x7fffffffe358 "monpdf.pdf") at toto.c:47
#1 0x0000000000400b7f in main (argc=2, argv=0x7fffffffdf08) at toto.c:88
gdb$ quit

- J'ai *beaucoup* de warning à la compilation.
gcc -W -Wall -ansi -pedantic toto.c -g
toto.c: In function ‘CountPDFPage’:
toto.c:28:9: warning: pointer targets in assignment differ in signedness [-Wpointer-sign]
toto.c:28:3: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
toto.c:32:5: warning: pointer targets in passing argument 1 of ‘atoi’ differ in signedness [-Wpointer-sign]
/usr/include/stdlib.h:148:12: note: expected ‘const char *’ but argument is of type ‘unsigned char *’
toto.c:36:7: warning: pointer targets in assignment differ in signedness [-Wpointer-sign]
toto.c:39:5: warning: pointer targets in passing argument 1 of ‘atoi’ differ in signedness [-Wpointer-sign]
/usr/include/stdlib.h:148:12: note: expected ‘const char *’ but argument is of type ‘unsigned char *’
toto.c:46:12: warning: pointer targets in assignment differ in signedness [-Wpointer-sign]
toto.c:46:32: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
toto.c:46:48: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
toto.c:47:7: warning: pointer targets in passing argument 1 of ‘atoi’ differ in signedness [-Wpointer-sign]
/usr/include/stdlib.h:148:12: note: expected ‘const char *’ but argument is of type ‘unsigned char *’
toto.c:56:13: warning: pointer targets in assignment differ in signedness [-Wpointer-sign]
toto.c:56:7: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
toto.c:58:9: warning: pointer targets in passing argument 1 of ‘strncmp’ differ in signedness [-Wpointer-sign]
/usr/include/string.h:146:12: note: expected ‘const char *’ but argument is of type ‘unsigned char *’
toto.c:60:15: warning: pointer targets in assignment differ in signedness [-Wpointer-sign]
toto.c:60:9: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
toto.c:66:17: warning: pointer targets in assignment differ in signedness [-Wpointer-sign]
toto.c:66:11: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
toto.c:68:13: warning: pointer targets in passing argument 1 of ‘atoi’ differ in signedness [-Wpointer-sign]
/usr/include/stdlib.h:148:12: note: expected ‘const char *’ but argument is of type ‘unsigned char *’
toto.c:13:16: warning: variable ‘len’ set but not used [-Wunused-but-set-variable]
toto.c: At top level:
toto.c:80:5: warning: first argument of ‘main’ should be ‘int’ [-Wmain]
Renfield
Messages postés
17283
Date d'inscription
mercredi 2 janvier 2002
Statut
Modérateur
Dernière intervention
21 juillet 2019
56 -
Corrigé.

Je cible aussi un systeme unix, avec cet outil, mes settings semblent etre plus laxistes que les tiens...
Je mets de coté cette -W -Wall pratique

Je m'en sert au boulot, dans des chaines de traitement (je sais donc que j'ai des pdf en entrée)

les xref compressées que proposent les specs pdf 1.5 ne sont pas prises en compte, ca peut etre une evolution a prévoir.

quand au fait que le fichier ne soit pas un pdf, il ne se passera pas grand chose, on ne trouvera pas de startxref en fin de fichier, so...
j'ai ajouté un test supplémentaire néanmoins
cptpingu
Messages postés
3821
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
28 août 2019
85 -
Il reste encore un tout petit peu de warnings :)

toto.c: In function ‘CountPDFPage’:
toto.c:48:32: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
toto.c:48:48: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] (p et buf étant de même type (char*), les cast en unsigned int, sont-ils nécessaires pour effectuer "p < buf" ?)
toto.c:11:16: warning: variable ‘len’ set but not used [-Wunused-but-set-variable] (ligne 47, len est remplie mais jamais utilisé, peut être p<(buf+len); plutôt que p<(buf+1024) ?)

Autre question:
- Pourquoi avoir "casté" le retour du malloc ligne 44 ? En C, ce n'est pas nécessaire (uniquement en C++).- refCount atoi(p)-1;> Peut être tester le atoi(p) avant de faire un -1 dans un unsigned ? (Chez moi le atoi(p) renvoie 0 et j'ai alors une valeur de refCount gigantesque, d'où mon segfault).

>> les xref compressées que proposent les specs pdf 1.5 ne sont pas prises en compte, ca peut etre une evolution a prévoir.
Y aurait-il un moyen de voir si le fichier est compressé et avertir l'utilisateur plutôt qu'un segfault ?

En tout cas merci pour les modifications, c'est le genre de petit outil dont je n'ai pas encore l'utilité, mais que je vais garder sous le coude !
Renfield
Messages postés
17283
Date d'inscription
mercredi 2 janvier 2002
Statut
Modérateur
Dernière intervention
21 juillet 2019
56 -
Je compilais sans le moindre Warning.... :(

c'est normalement corrigé, plus bavard et plus les pdf mieux controlés.
Merci pour ces tests rapides et efficaces.
cptpingu
Messages postés
3821
Date d'inscription
dimanche 12 décembre 2004
Statut
Modérateur
Dernière intervention
28 août 2019
85 -
Plus de warning et exécution impeccable :)

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.