From d581dc124e43dd6bcfbfebd590682a0a5159ef23 Mon Sep 17 00:00:00 2001 From: Horst Rutter Date: Sun, 3 Mar 2024 04:23:52 +0100 Subject: [PATCH] Fix #809 --- pkg/cli/list.go | 81 ++++++++++++++++++++++++++++++++++++++- pkg/pdfcpu/info.go | 72 +++++++++++++++++----------------- pkg/pdfcpu/model/box.go | 33 ++++++++-------- pkg/pdfcpu/types/types.go | 39 +++++++++++++++++-- 4 files changed, 168 insertions(+), 57 deletions(-) diff --git a/pkg/cli/list.go b/pkg/cli/list.go index 08e9d044..60358f32 100644 --- a/pkg/cli/list.go +++ b/pkg/cli/list.go @@ -21,8 +21,10 @@ import ( "encoding/json" "fmt" "io" + "math" "os" "sort" + "strconv" "time" "github.com/pdfcpu/pdfcpu/pkg/api" @@ -30,6 +32,7 @@ import ( "github.com/pdfcpu/pdfcpu/pkg/pdfcpu" "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/form" "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model" + "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types" "github.com/pkg/errors" ) @@ -282,6 +285,73 @@ func ListInfoFile(inFile string, selectedPages []string, conf *model.Configurati return append([]string{inFile + ":"}, ss...), err } +func jsonInfo(info *pdfcpu.PDFInfo, pages types.IntSet) (map[string]model.PageBoundaries, []types.Dim) { + if len(pages) > 0 { + pbs := map[string]model.PageBoundaries{} + for i, pb := range info.PageBoundaries { + if _, found := pages[i+1]; !found { + continue + } + d := pb.CropBox().Dimensions() + if pb.Rot%180 != 0 { + d.Width, d.Height = d.Height, d.Width + } + pb.Orientation = "portrait" + if d.Landscape() { + pb.Orientation = "landscape" + } + if pb.Media != nil { + pb.Media.Rect = pb.Media.Rect.ConvertToUnit(info.Unit) + pb.Media.Rect.LL.X = math.Round(pb.Media.Rect.LL.X*100) / 100 + pb.Media.Rect.LL.Y = math.Round(pb.Media.Rect.LL.Y*100) / 100 + pb.Media.Rect.UR.X = math.Round(pb.Media.Rect.UR.X*100) / 100 + pb.Media.Rect.UR.Y = math.Round(pb.Media.Rect.UR.Y*100) / 100 + } + if pb.Crop != nil { + pb.Crop.Rect = pb.Crop.Rect.ConvertToUnit(info.Unit) + pb.Crop.Rect.LL.X = math.Round(pb.Crop.Rect.LL.X*100) / 100 + pb.Crop.Rect.LL.Y = math.Round(pb.Crop.Rect.LL.Y*100) / 100 + pb.Crop.Rect.UR.X = math.Round(pb.Crop.Rect.UR.X*100) / 100 + pb.Crop.Rect.UR.Y = math.Round(pb.Crop.Rect.UR.Y*100) / 100 + } + if pb.Trim != nil { + pb.Trim.Rect = pb.Trim.Rect.ConvertToUnit(info.Unit) + pb.Trim.Rect.LL.X = math.Round(pb.Trim.Rect.LL.X*100) / 100 + pb.Trim.Rect.LL.Y = math.Round(pb.Trim.Rect.LL.Y*100) / 100 + pb.Trim.Rect.UR.X = math.Round(pb.Trim.Rect.UR.X*100) / 100 + pb.Trim.Rect.UR.Y = math.Round(pb.Trim.Rect.UR.Y*100) / 100 + } + if pb.Bleed != nil { + pb.Bleed.Rect = pb.Bleed.Rect.ConvertToUnit(info.Unit) + pb.Bleed.Rect.LL.X = math.Round(pb.Bleed.Rect.LL.X*100) / 100 + pb.Bleed.Rect.LL.Y = math.Round(pb.Bleed.Rect.LL.Y*100) / 100 + pb.Bleed.Rect.UR.X = math.Round(pb.Bleed.Rect.UR.X*100) / 100 + pb.Bleed.Rect.UR.Y = math.Round(pb.Bleed.Rect.UR.Y*100) / 100 + } + if pb.Art != nil { + pb.Art.Rect = pb.Art.Rect.ConvertToUnit(info.Unit) + pb.Art.Rect.LL.X = math.Round(pb.Art.Rect.LL.X*100) / 100 + pb.Art.Rect.LL.Y = math.Round(pb.Art.Rect.LL.Y*100) / 100 + pb.Art.Rect.UR.X = math.Round(pb.Art.Rect.UR.X*100) / 100 + pb.Art.Rect.UR.Y = math.Round(pb.Art.Rect.UR.Y*100) / 100 + } + pbs[strconv.Itoa(i+1)] = pb + } + return pbs, nil + } + + var dims []types.Dim + for k, v := range info.PageDimensions { + if v { + dc := k.ConvertToUnit(info.Unit) + dc.Width = math.Round(dc.Width*100) / 100 + dc.Height = math.Round(dc.Height*100) / 100 + dims = append(dims, dc) + } + } + return nil, dims +} + func listInfoFilesJSON(inFiles []string, selectedPages []string, conf *model.Configuration) ([]string, error) { var infos []*pdfcpu.PDFInfo @@ -298,12 +368,19 @@ func listInfoFilesJSON(inFiles []string, selectedPages []string, conf *model.Con return nil, err } + pages, err := api.PagesForPageSelection(info.PageCount, selectedPages, false, false) + if err != nil { + return nil, err + } + + info.Boundaries, info.Dimensions = jsonInfo(info, pages) + infos = append(infos, info) } s := struct { - Header pdfcpu.Header `json:"header"` - Infos []*pdfcpu.PDFInfo + Header pdfcpu.Header `json:"header"` + Infos []*pdfcpu.PDFInfo `json:"infos"` }{ Header: pdfcpu.Header{Version: "pdfcpu " + model.VersionStr, Creation: time.Now().Format("2006-01-02 15:04:05 MST")}, Infos: infos, diff --git a/pkg/pdfcpu/info.go b/pkg/pdfcpu/info.go index 4e66edf5..665b010d 100644 --- a/pkg/pdfcpu/info.go +++ b/pkg/pdfcpu/info.go @@ -299,7 +299,7 @@ func pageInfo(info *PDFInfo, selectedPages types.IntSet) ([]string, error) { return ss, nil } - s := "Page size:" + s := "Page sizes:" for d := range info.PageDimensions { dc := d.ConvertToUnit(info.Unit) ss = append(ss, fmt.Sprintf("%21s %.2f x %.2f %s", s, dc.Width, dc.Height, info.UnitString)) @@ -309,40 +309,42 @@ func pageInfo(info *PDFInfo, selectedPages types.IntSet) ([]string, error) { } type PDFInfo struct { - FileName string `json:"source,omitempty"` - Version string `json:"version"` - PageCount int `json:"pages"` - PageBoundaries []model.PageBoundaries `json:"-"` - PageDimensions map[types.Dim]bool `json:"-"` - Title string `json:"title"` - Author string `json:"author"` - Subject string `json:"subject"` - Producer string `json:"producer"` - Creator string `json:"creator"` - CreationDate string `json:"creationDate"` - ModificationDate string `json:"modificationDate"` - PageMode string `json:"pageMode,omitempty"` - PageLayout string `json:"pageLayout,omitempty"` - ViewerPref *model.ViewerPreferences `json:"viewerPreferences,omitempty"` - Keywords []string `json:"keywords"` - Properties map[string]string `json:"properties"` - Tagged bool `json:"tagged"` - Hybrid bool `json:"hybrid"` - Linearized bool `json:"linearized"` - UsingXRefStreams bool `json:"usingXRefStreams"` - UsingObjectStreams bool `json:"usingObjectStreams"` - Watermarked bool `json:"watermarked"` - Thumbnails bool `json:"thumbnails"` - Form bool `json:"form"` - Signatures bool `json:"signatures"` - AppendOnly bool `json:"appendOnly"` - Outlines bool `json:"bookmarks"` - Names bool `json:"names"` - Encrypted bool `json:"encrypted"` - Permissions int `json:"permissions"` - Attachments []model.Attachment `json:"attachments,omitempty"` - Unit types.DisplayUnit `json:"-"` - UnitString string `json:"-"` + FileName string `json:"source,omitempty"` + Version string `json:"version"` + PageCount int `json:"pageCount"` + PageBoundaries []model.PageBoundaries `json:"-"` + Boundaries map[string]model.PageBoundaries `json:"pageBoundaries,omitempty"` + PageDimensions map[types.Dim]bool `json:"-"` + Dimensions []types.Dim `json:"pageSizes,omitempty"` + Title string `json:"title"` + Author string `json:"author"` + Subject string `json:"subject"` + Producer string `json:"producer"` + Creator string `json:"creator"` + CreationDate string `json:"creationDate"` + ModificationDate string `json:"modificationDate"` + PageMode string `json:"pageMode,omitempty"` + PageLayout string `json:"pageLayout,omitempty"` + ViewerPref *model.ViewerPreferences `json:"viewerPreferences,omitempty"` + Keywords []string `json:"keywords"` + Properties map[string]string `json:"properties"` + Tagged bool `json:"tagged"` + Hybrid bool `json:"hybrid"` + Linearized bool `json:"linearized"` + UsingXRefStreams bool `json:"usingXRefStreams"` + UsingObjectStreams bool `json:"usingObjectStreams"` + Watermarked bool `json:"watermarked"` + Thumbnails bool `json:"thumbnails"` + Form bool `json:"form"` + Signatures bool `json:"signatures"` + AppendOnly bool `json:"appendOnly"` + Outlines bool `json:"bookmarks"` + Names bool `json:"names"` + Encrypted bool `json:"encrypted"` + Permissions int `json:"permissions"` + Attachments []model.Attachment `json:"attachments,omitempty"` + Unit types.DisplayUnit `json:"-"` + UnitString string `json:"unit"` } func (info PDFInfo) renderKeywords(ss *[]string) error { diff --git a/pkg/pdfcpu/model/box.go b/pkg/pdfcpu/model/box.go index 797def59..1e0d042f 100644 --- a/pkg/pdfcpu/model/box.go +++ b/pkg/pdfcpu/model/box.go @@ -31,27 +31,28 @@ import ( // Media box serves as parent box for crop box. // Crop box serves as parent box for trim, bleed and art box. type Box struct { - Rect *types.Rectangle // Rectangle in user space. - Inherited bool // Media box and Crop box may be inherited. - RefBox string // Use position of another box, + Rect *types.Rectangle `json:"rect"` // Rectangle in user space. + Inherited bool `json:"-"` // Media box and Crop box may be inherited. + RefBox string `json:"-"` // Use position of another box, // Margins to parent box in points. // Relative to parent box if 0 < x < 0.5 - MLeft, MRight float64 - MTop, MBot float64 + MLeft, MRight float64 `json:"-"` + MTop, MBot float64 `json:"-"` // Relative position within parent box - Dim *types.Dim // dimensions - Pos types.Anchor // position anchor within parent box, one of tl,tc,tr,l,c,r,bl,bc,br. - Dx, Dy int // anchor offset + Dim *types.Dim `json:"-"` // dimensions + Pos types.Anchor `json:"-"` // position anchor within parent box, one of tl,tc,tr,l,c,r,bl,bc,br. + Dx, Dy int `json:"-"` // anchor offset } // PageBoundaries represent the defined PDF page boundaries. type PageBoundaries struct { - Media *Box - Crop *Box - Trim *Box - Bleed *Box - Art *Box - Rot int // The effective page rotation. + Media *Box `json:"mediaBox,omitempty"` + Crop *Box `json:"cropBox,omitempty"` + Trim *Box `json:"trimBox,omitempty"` + Bleed *Box `json:"bleedBox,omitempty"` + Art *Box `json:"artBox,omitempty"` + Rot int `json:"rot"` // The effective page rotation. + Orientation string `json:"orient"` } // SelectAll selects all page boundaries. @@ -727,7 +728,7 @@ func parseBoxDim(s string, b *Box, u types.DisplayUnit) error { return nil } -func parseBoxByPosWithinParent(s string, ss []string, u types.DisplayUnit) (*Box, error) { +func parseBoxByPosWithinParent(ss []string, u types.DisplayUnit) (*Box, error) { b := &Box{Pos: types.Center} for _, s := range ss { @@ -827,7 +828,7 @@ func ParseBox(s string, u types.DisplayUnit) (*Box, error) { return nil, errors.Errorf("pdfcpu: invalid box definition: %s", s) } if len(ss) > 1 || strings.HasPrefix(ss[0], "dim") { - return parseBoxByPosWithinParent(s, ss, u) + return parseBoxByPosWithinParent(ss, u) } // Via margins relative to parent box. diff --git a/pkg/pdfcpu/types/types.go b/pkg/pdfcpu/types/types.go index 961a9945..dcc79774 100644 --- a/pkg/pdfcpu/types/types.go +++ b/pkg/pdfcpu/types/types.go @@ -146,7 +146,8 @@ func (i Integer) Value() int { // Point represents a user space location. type Point struct { - X, Y float64 + X float64 `json:"x"` + Y float64 `json:"y"` } // Translate modifies p's coordinates. @@ -161,7 +162,8 @@ func (p Point) String() string { // Rectangle represents a rectangular region in userspace. type Rectangle struct { - LL, UR Point + LL Point `json:"ll"` + UR Point `json:"ur"` } // NewRectangle returns a new rectangle for given corner coordinates. @@ -274,6 +276,34 @@ func (r Rectangle) CroppedCopy(margin float64) *Rectangle { return NewRectangle(r.LL.X+margin, r.LL.Y+margin, r.UR.X-margin, r.UR.Y-margin) } +// ToInches converts r to inches. +func (r Rectangle) ToInches() *Rectangle { + return NewRectangle(r.LL.X*userSpaceToInch, r.LL.Y*userSpaceToInch, r.UR.X*userSpaceToInch, r.UR.Y*userSpaceToInch) +} + +// ToCentimetres converts r to centimetres. +func (r Rectangle) ToCentimetres() *Rectangle { + return NewRectangle(r.LL.X*userSpaceToCm, r.LL.Y*userSpaceToCm, r.UR.X*userSpaceToCm, r.UR.Y*userSpaceToCm) +} + +// ToMillimetres converts r to millimetres. +func (r Rectangle) ToMillimetres() *Rectangle { + return NewRectangle(r.LL.X*userSpaceToMm, r.LL.Y*userSpaceToMm, r.UR.X*userSpaceToMm, r.UR.Y*userSpaceToMm) +} + +// ConvertToUnit converts r to unit. +func (r *Rectangle) ConvertToUnit(unit DisplayUnit) *Rectangle { + switch unit { + case INCHES: + return r.ToInches() + case CENTIMETRES: + return r.ToCentimetres() + case MILLIMETRES: + return r.ToMillimetres() + } + return r +} + func (r Rectangle) formatToInches() string { return fmt.Sprintf("(%3.2f, %3.2f, %3.2f, %3.2f) w=%.2f h=%.2f ar=%.2f", r.LL.X*userSpaceToInch, @@ -524,7 +554,8 @@ func ToUserSpace(f float64, unit DisplayUnit) float64 { // like a PDF page, a sheet of paper or an image grid // in user space, inches, centimetres or millimetres. type Dim struct { - Width, Height float64 + Width float64 `json:"width"` + Height float64 `json:"height"` } // ToInches converts d to inches. @@ -537,7 +568,7 @@ func (d Dim) ToCentimetres() Dim { return Dim{d.Width * userSpaceToCm, d.Height * userSpaceToCm} } -// ToMillimetres converts d to centimetres. +// ToMillimetres converts d to millimetres. func (d Dim) ToMillimetres() Dim { return Dim{d.Width * userSpaceToMm, d.Height * userSpaceToMm} }