fix(pdfcpu): correct sorting for the output paths of the split method

This commit is contained in:
Julien Neuhart
2025-02-12 11:47:45 +01:00
parent 3dc8c18849
commit 80f3f89a89
7 changed files with 77 additions and 157 deletions

View File

@@ -7,7 +7,8 @@ import (
)
// AlphanumericSort implements sort.Interface and helps to sort strings
// alphanumerically.
// alphanumerically by either a numeric prefix or, if missing, a numeric
// suffix.
//
// See: https://github.com/gotenberg/gotenberg/issues/805.
type AlphanumericSort []string
@@ -21,20 +22,20 @@ func (s AlphanumericSort) Swap(i, j int) {
}
func (s AlphanumericSort) Less(i, j int) bool {
numI, restI := extractPrefix(s[i])
numJ, restJ := extractPrefix(s[j])
numI, restI := extractNumber(s[i])
numJ, restJ := extractNumber(s[j])
// Compares numerical prefixes if they exist.
// If both strings contain a number, compare them numerically.
if numI != -1 && numJ != -1 {
if numI != numJ {
return numI < numJ
}
// If numbers are equal, falls back to string comparison of the rest.
// If the numbers are equal, compare the "rest" strings.
return restI < restJ
}
// If one has a numerical prefix and the other doesn't, the one with the
// number comes first.
// If one contains a number and the other doesn't, the one with the number
// comes first.
if numI != -1 {
return true
}
@@ -42,28 +43,52 @@ func (s AlphanumericSort) Less(i, j int) bool {
return false
}
// If neither has a numerical prefix, compare as strings
// Neither has a number; fall back to lexicographical order.
return s[i] < s[j]
}
// extractPrefix attempts to extract a numerical prefix and the rest of the filename
func extractPrefix(filename string) (int, string) {
matches := numPrefixRegexp.FindStringSubmatch(filename)
if len(matches) > 2 {
prefix, err := strconv.Atoi(matches[1])
if err == nil {
return prefix, matches[2]
// extractNumber attempts to extract a numeric portion from the filename.
// It first checks for a numeric prefix (digits at the beginning).
// If none is found, it next attempts to match a number immediately before the
// extension (for filenames such as "sample1_1.pdf").
// If that fails, it then attempts a trailing numeric pattern.
// If no number is found, it returns -1 and the original string.
func extractNumber(str string) (int, string) {
// Check for a numeric prefix.
if matches := prefixRegexp.FindStringSubmatch(str); len(matches) > 2 {
if num, err := strconv.Atoi(matches[1]); err == nil {
return num, matches[2]
}
}
// Returns -1 if no numerical prefix is found, indicating to just compare
// as strings.
return -1, filename
// Check for a number immediately before an extension.
if matches := extensionSuffixRegexp.FindStringSubmatch(str); len(matches) > 3 {
if num, err := strconv.Atoi(matches[2]); err == nil {
// Remove the numeric block but keep the extension.
return num, matches[1] + matches[3]
}
}
// Check for a trailing number (with no extension following).
if matches := suffixRegexp.FindStringSubmatch(str); len(matches) > 2 {
if num, err := strconv.Atoi(matches[2]); err == nil {
return num, matches[1]
}
}
// No numeric portion found.
return -1, str
}
var numPrefixRegexp = regexp.MustCompile(`^(\d+)(.*)$`)
// Regular expressions used by extractNumber.
var (
// Matches a numeric prefix: one or more digits at the start.
prefixRegexp = regexp.MustCompile(`^(\d+)(.*)$`)
// Matches a numeric block immediately before a file extension.
extensionSuffixRegexp = regexp.MustCompile(`^(.*?)(\d+)(\.[^.]+)$`)
// Matches a trailing numeric sequence when there is no extension.
suffixRegexp = regexp.MustCompile(`^(.*?)(\d+)$`)
)
// Interface guard.
var (
_ sort.Interface = (*AlphanumericSort)(nil)
)
var _ sort.Interface = (*AlphanumericSort)(nil)