2 Star 0 Fork 1

Deeao/golang-pdfcpu

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
selectPages.go 13.23 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
/*
Copyright 2018 The pdfcpu Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package api
import (
"fmt"
"regexp"
"sort"
"strconv"
"strings"
"gitee.com/deeao/golang-pdfcpu/pkg/log"
"gitee.com/deeao/golang-pdfcpu/pkg/pdfcpu/types"
"github.com/pkg/errors"
)
var (
selectedPagesRegExp *regexp.Regexp
)
func setupRegExpForPageSelection() *regexp.Regexp {
e := "(\\d+)?-l(-\\d+)?|l(-(\\d+)-?)?"
e = "[!n]?((-\\d+)|(\\d+(-(\\d+)?)?)|" + e + ")"
e = "\\Qeven\\E|\\Qodd\\E|" + e
exp := "^" + e + "(," + e + ")*$"
re, _ := regexp.Compile(exp)
return re
}
func init() {
selectedPagesRegExp = setupRegExpForPageSelection()
}
// ParsePageSelection ensures a correct page selection expression.
func ParsePageSelection(s string) ([]string, error) {
if s == "" {
return nil, nil
}
// Ensure valid comma separated expression of:{ {even|odd}{!}{-}# | {even|odd}{!}#-{#} }*
//
// Negated expressions:
// '!' negates an expression
// since '!' needs to be part of a single quoted string in bash
// as an alternative also 'n' works instead of "!"
//
// Extract all but page 4 may be expressed as: "1-,!4" or "1-,n4"
//
// The pageSelection is evaluated strictly from left to right!
// e.g. "!3,1-5" extracts pages 1-5 whereas "1-5,!3" extracts pages 1,2,4,5
//
if !selectedPagesRegExp.MatchString(s) {
return nil, errors.Errorf("-pages \"%s\" => syntax error\n", s)
}
//log.CLI.Printf("pageSelection: %s\n", s)
return strings.Split(s, ","), nil
}
func handlePrefix(v string, negated bool, pageCount int, selectedPages types.IntSet) error {
// -l
if v == "l" {
for j := 1; j <= pageCount; j++ {
selectedPages[j] = !negated
}
return nil
}
// -l-#
if strings.HasPrefix(v, "l-") {
i, err := strconv.Atoi(v[2:])
if err != nil {
return err
}
if pageCount-i < 1 {
return nil
}
for j := 1; j <= pageCount-i; j++ {
selectedPages[j] = !negated
}
return nil
}
// -#
i, err := strconv.Atoi(v)
if err != nil {
return err
}
// Handle overflow gracefully
if i > pageCount {
i = pageCount
}
// identified
// -# ... select all pages up to and including #
// or !-# ... deselect all pages up to and including #
for j := 1; j <= i; j++ {
selectedPages[j] = !negated
}
return nil
}
func handleSuffix(v string, negated bool, pageCount int, selectedPages types.IntSet) error {
// must be #- ... select all pages from here until the end.
// or !#- ... deselect all pages from here until the end.
i, err := strconv.Atoi(v)
if err != nil {
return err
}
// Handle overflow gracefully
if i > pageCount {
return nil
}
for j := i; j <= pageCount; j++ {
selectedPages[j] = !negated
}
return nil
}
func handleSpecificPageOrLastXPages(s string, negated bool, pageCount int, selectedPages types.IntSet) error {
// l
if s == "l" {
selectedPages[pageCount] = !negated
return nil
}
// l-#
if strings.HasPrefix(s, "l-") {
pr := strings.Split(s[2:], "-")
i, err := strconv.Atoi(pr[0])
if err != nil {
return err
}
if pageCount-i < 1 {
return nil
}
j := pageCount - i
// l-#-
if strings.HasSuffix(s, "-") {
j = pageCount
}
for i := pageCount - i; i <= j; i++ {
selectedPages[i] = !negated
}
return nil
}
// must be # ... select a specific page
// or !# ... deselect a specific page
i, err := strconv.Atoi(s)
if err != nil {
return err
}
// Handle overflow gracefully
if i > pageCount {
return nil
}
selectedPages[i] = !negated
return nil
}
func negation(c byte) bool {
return c == '!' || c == 'n'
}
func selectEvenPages(selectedPages types.IntSet, pageCount int) {
for i := 2; i <= pageCount; i += 2 {
_, found := selectedPages[i]
if !found {
selectedPages[i] = true
}
}
}
func selectOddPages(selectedPages types.IntSet, pageCount int) {
for i := 1; i <= pageCount; i += 2 {
_, found := selectedPages[i]
if !found {
selectedPages[i] = true
}
}
}
func parsePageRange(pr []string, pageCount int, negated bool, selectedPages types.IntSet) error {
from, err := strconv.Atoi(pr[0])
if err != nil {
return err
}
// Handle overflow gracefully
if from > pageCount {
return nil
}
var thru int
if pr[1] == "l" {
// #-l
thru = pageCount
if len(pr) == 3 {
// #-l-#
i, err := strconv.Atoi(pr[2])
if err != nil {
return err
}
thru -= i
}
} else {
// #-#
var err error
thru, err = strconv.Atoi(pr[1])
if err != nil {
return err
}
}
// Handle overflow gracefully
if thru < from {
return nil
}
if thru > pageCount {
thru = pageCount
}
for i := from; i <= thru; i++ {
selectedPages[i] = !negated
}
return nil
}
func sortedPages(selectedPages types.IntSet) []int {
p := []int(nil)
for i, v := range selectedPages {
if v {
p = append(p, i)
}
}
sort.Ints(p)
return p
}
func logSelPages(selectedPages types.IntSet) {
if !log.CLIEnabled() || len(selectedPages) == 0 {
return
}
var b strings.Builder
for _, i := range sortedPages(selectedPages) {
fmt.Fprintf(&b, "%d,", i)
}
s := b.String()
if len(s) > 1 {
s = s[:len(s)-1]
}
// TODO Suppress for multifile cmds
if log.CLIEnabled() {
log.CLI.Printf("pages: %s\n", s)
}
}
func calcSelPages(pageCount int, pageSelection []string, selectedPages types.IntSet) error {
for _, v := range pageSelection {
//log.Stats.Printf("pageExp: <%s>\n", v)
if v == "even" {
selectEvenPages(selectedPages, pageCount)
continue
}
if v == "odd" {
selectOddPages(selectedPages, pageCount)
continue
}
var negated bool
if negation(v[0]) {
negated = true
//logInfoAPI.Printf("is a negated exp\n")
v = v[1:]
}
// -#
if v[0] == '-' {
v = v[1:]
if err := handlePrefix(v, negated, pageCount, selectedPages); err != nil {
return err
}
continue
}
// #-
if v[0] != 'l' && strings.HasSuffix(v, "-") {
if err := handleSuffix(v[:len(v)-1], negated, pageCount, selectedPages); err != nil {
return err
}
continue
}
// l l-# l-#-
if v[0] == 'l' {
if err := handleSpecificPageOrLastXPages(v, negated, pageCount, selectedPages); err != nil {
return err
}
continue
}
pr := strings.Split(v, "-")
if len(pr) >= 2 {
// v contains '-' somewhere in the middle
// #-# #-l #-l-#
if err := parsePageRange(pr, pageCount, negated, selectedPages); err != nil {
return err
}
continue
}
// #
if err := handleSpecificPageOrLastXPages(pr[0], negated, pageCount, selectedPages); err != nil {
return err
}
}
return nil
}
// selectedPages returns a set of used page numbers.
// key==page# => key 0 unused!
func selectedPages(pageCount int, pageSelection []string, log bool) (types.IntSet, error) {
selectedPages := types.IntSet{}
if err := calcSelPages(pageCount, pageSelection, selectedPages); err != nil {
return nil, err
}
if log {
logSelPages(selectedPages)
}
return selectedPages, nil
}
// PagesForPageSelection ensures a set of page numbers for an ascending page sequence
// where each page number may appear only once.
func PagesForPageSelection(pageCount int, pageSelection []string, ensureAllforNone bool, log bool) (types.IntSet, error) {
if len(pageSelection) > 0 {
return selectedPages(pageCount, pageSelection, log)
}
if !ensureAllforNone {
//log.CLI.Printf("pages: none\n")
return nil, nil
}
m := types.IntSet{}
for i := 1; i <= pageCount; i++ {
m[i] = true
}
//log.CLI.Printf("pages: all\n")
return m, nil
}
func RemainingPagesForPageRemoval(pageCount int, pageSelection []string, log bool) (types.IntSet, error) {
pagesToRemove, err := selectedPages(pageCount, pageSelection, log)
if err != nil {
return nil, err
}
m := types.IntSet{}
for i := 1; i <= pageCount; i++ {
m[i] = true
}
for k, v := range pagesToRemove {
if v {
m[k] = false
}
}
return m, nil
}
func deletePageFromCollection(cp *[]int, p int) {
a := []int{}
for _, i := range *cp {
if i != p {
a = append(a, i)
}
}
*cp = a
}
func processPageForCollection(cp *[]int, negated bool, i int) {
if !negated {
*cp = append(*cp, i)
} else {
deletePageFromCollection(cp, i)
}
}
func collectEvenPages(cp *[]int, pageCount int) {
for i := 2; i <= pageCount; i += 2 {
*cp = append(*cp, i)
}
}
func collectOddPages(cp *[]int, pageCount int) {
for i := 1; i <= pageCount; i += 2 {
*cp = append(*cp, i)
}
}
func handlePrefixForCollection(v string, negated bool, pageCount int, cp *[]int) error {
// -l
if v == "l" {
for j := 1; j <= pageCount; j++ {
processPageForCollection(cp, negated, j)
}
return nil
}
// -l-#
if strings.HasPrefix(v, "l-") {
i, err := strconv.Atoi(v[2:])
if err != nil {
return err
}
if pageCount-i < 1 {
return nil
}
for j := 1; j <= pageCount-i; j++ {
processPageForCollection(cp, negated, j)
}
return nil
}
// -#
i, err := strconv.Atoi(v)
if err != nil {
return err
}
// Handle overflow gracefully
if i > pageCount {
i = pageCount
}
// identified
// -# ... select all pages up to and including #
// or !-# ... deselect all pages up to and including #
for j := 1; j <= i; j++ {
processPageForCollection(cp, negated, j)
}
return nil
}
func handleSuffixForCollection(v string, negated bool, pageCount int, cp *[]int) error {
// must be #- ... select all pages from here until the end.
// or !#- ... deselect all pages from here until the end.
i, err := strconv.Atoi(v)
if err != nil {
return err
}
// Handle overflow gracefully
if i > pageCount {
return nil
}
for j := i; j <= pageCount; j++ {
processPageForCollection(cp, negated, j)
}
return nil
}
func handleSpecificPageOrLastXPagesForCollection(s string, negated bool, pageCount int, cp *[]int) error {
// l
if s == "l" {
processPageForCollection(cp, negated, pageCount)
return nil
}
// l-#
if strings.HasPrefix(s, "l-") {
pr := strings.Split(s[2:], "-")
i, err := strconv.Atoi(pr[0])
if err != nil {
return err
}
if pageCount-i < 1 {
return nil
}
j := pageCount - i
// l-#-
if strings.HasSuffix(s, "-") {
j = pageCount
}
for i := pageCount - i; i <= j; i++ {
processPageForCollection(cp, negated, i)
}
return nil
}
// must be # ... select a specific page
// or !# ... deselect a specific page
i, err := strconv.Atoi(s)
if err != nil {
return err
}
// Handle overflow gracefully
if i > pageCount {
return nil
}
processPageForCollection(cp, negated, i)
return nil
}
func parsePageRangeForCollection(pr []string, pageCount int, negated bool, cp *[]int) error {
from, err := strconv.Atoi(pr[0])
if err != nil {
return err
}
// Handle overflow gracefully
if from > pageCount {
return nil
}
var thru int
if pr[1] == "l" {
// #-l
thru = pageCount
if len(pr) == 3 {
// #-l-#
i, err := strconv.Atoi(pr[2])
if err != nil {
return err
}
thru -= i
}
} else {
// #-#
var err error
thru, err = strconv.Atoi(pr[1])
if err != nil {
return err
}
}
// Handle overflow gracefully
if thru < from {
return nil
}
if thru > pageCount {
thru = pageCount
}
for i := from; i <= thru; i++ {
processPageForCollection(cp, negated, i)
}
return nil
}
// PagesForPageCollection returns a slice of page numbers for a page collection.
// Any page number in any order any number of times allowed.
func PagesForPageCollection(pageCount int, pageSelection []string) ([]int, error) {
collectedPages := []int{}
for _, v := range pageSelection {
if v == "even" {
collectEvenPages(&collectedPages, pageCount)
continue
}
if v == "odd" {
collectOddPages(&collectedPages, pageCount)
continue
}
var negated bool
if negation(v[0]) {
negated = true
//logInfoAPI.Printf("is a negated exp\n")
v = v[1:]
}
// -#
if v[0] == '-' {
v = v[1:]
if err := handlePrefixForCollection(v, negated, pageCount, &collectedPages); err != nil {
return nil, err
}
continue
}
// #-
if v[0] != 'l' && strings.HasSuffix(v, "-") {
if err := handleSuffixForCollection(v[:len(v)-1], negated, pageCount, &collectedPages); err != nil {
return nil, err
}
continue
}
// l l-# l-#-
if v[0] == 'l' {
if err := handleSpecificPageOrLastXPagesForCollection(v, negated, pageCount, &collectedPages); err != nil {
return nil, err
}
continue
}
pr := strings.Split(v, "-")
if len(pr) >= 2 {
// v contains '-' somewhere in the middle
// #-# #-l #-l-#
if err := parsePageRangeForCollection(pr, pageCount, negated, &collectedPages); err != nil {
return nil, err
}
continue
}
// #
if err := handleSpecificPageOrLastXPagesForCollection(pr[0], negated, pageCount, &collectedPages); err != nil {
return nil, err
}
}
if len(collectedPages) == 0 {
return nil, errors.Errorf("pdfcpu: no page selected")
}
return collectedPages, nil
}
// PagesForPageRange returns a slice of page numbers for a page range.
func PagesForPageRange(from, thru int) []int {
s := make([]int, thru-from+1)
for i := 0; i < len(s); i++ {
s[i] = from + i
}
return s
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/deeao/golang-pdfcpu.git
git@gitee.com:deeao/golang-pdfcpu.git
deeao
golang-pdfcpu
golang-pdfcpu
v1.0.2

搜索帮助