2019-07-14 23:18:40 +02:00
|
|
|
/*
|
|
|
|
* This file is subject to the terms and conditions defined in
|
|
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package jbig2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2019-08-29 21:12:18 +02:00
|
|
|
"math"
|
2019-07-14 23:18:40 +02:00
|
|
|
|
|
|
|
"github.com/unidoc/unipdf/v3/common"
|
|
|
|
|
|
|
|
"github.com/unidoc/unipdf/v3/internal/jbig2/bitmap"
|
|
|
|
"github.com/unidoc/unipdf/v3/internal/jbig2/segments"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Page represents JBIG2 Page structure.
|
|
|
|
// It contains all the included segments header definitions mapped to
|
|
|
|
// their number relation to the document and the resultant page bitmap.
|
|
|
|
type Page struct {
|
|
|
|
// Segments relation of the page number to their structures.
|
|
|
|
Segments map[int]*segments.Header
|
|
|
|
|
|
|
|
// PageNumber defines page number.
|
|
|
|
// NOTE: page numeration starts from 1.
|
|
|
|
PageNumber int
|
|
|
|
|
|
|
|
// Bitmap represents the page image.
|
|
|
|
Bitmap *bitmap.Bitmap
|
|
|
|
|
|
|
|
FinalHeight int
|
|
|
|
FinalWidth int
|
|
|
|
ResolutionX int
|
|
|
|
ResolutionY int
|
|
|
|
|
|
|
|
// Document is a relation to page's document
|
|
|
|
Document *Document
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBitmap implements segments.Pager interface.
|
|
|
|
func (p *Page) GetBitmap() (bm *bitmap.Bitmap, err error) {
|
|
|
|
common.Log.Trace(fmt.Sprintf("[PAGE][#%d] GetBitmap begins...", p.PageNumber))
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
common.Log.Trace(fmt.Sprintf("[PAGE][#%d] GetBitmap failed. %v", p.PageNumber, err))
|
|
|
|
} else {
|
|
|
|
common.Log.Trace(fmt.Sprintf("[PAGE][#%d] GetBitmap finished", p.PageNumber))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
if p.Bitmap != nil {
|
|
|
|
return p.Bitmap, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err = p.composePageBitmap()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return p.Bitmap, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSegment implements segments.Pager interface.
|
|
|
|
func (p *Page) GetSegment(number int) *segments.Header {
|
|
|
|
s, ok := p.Segments[number]
|
|
|
|
if ok {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ok || s == nil {
|
|
|
|
s, _ = p.Document.GlobalSegments.GetSegment(number)
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
common.Log.Info("Segment not found, returning nil.")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// String implements Stringer interface.
|
|
|
|
func (p *Page) String() string {
|
|
|
|
return fmt.Sprintf("Page #%d", p.PageNumber)
|
|
|
|
}
|
|
|
|
|
|
|
|
// newPage is the creator for the Page structure.
|
|
|
|
func newPage(d *Document, pageNumber int) *Page {
|
|
|
|
return &Page{Document: d, PageNumber: pageNumber, Segments: map[int]*segments.Header{}}
|
|
|
|
}
|
|
|
|
|
|
|
|
// composePageBitmap composes the segment's bitmaps
|
|
|
|
// as a single page Bitmap.
|
|
|
|
func (p *Page) composePageBitmap() error {
|
|
|
|
if p.PageNumber == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
h := p.getPageInformationSegment()
|
|
|
|
if h == nil {
|
|
|
|
return errors.New("Page Information segment not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the Segment data
|
|
|
|
seg, err := h.GetSegmentData()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
pageInformation, ok := seg.(*segments.PageInformationSegment)
|
|
|
|
if !ok {
|
|
|
|
return errors.New("PageInformation Segment is of invalid type")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = p.createPage(pageInformation); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p.clearSegmentData()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) createPage(i *segments.PageInformationSegment) error {
|
|
|
|
var err error
|
|
|
|
if !i.IsStripe || i.PageBMHeight != -1 {
|
|
|
|
// Page 79, 4)
|
|
|
|
err = p.createNormalPage(i)
|
|
|
|
} else {
|
|
|
|
err = p.createStripedPage(i)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) createNormalPage(i *segments.PageInformationSegment) error {
|
|
|
|
p.Bitmap = bitmap.New(i.PageBMWidth, i.PageBMHeight)
|
|
|
|
|
|
|
|
// Page 79, 3)
|
|
|
|
// if default pixel value is not 0, byte will be filled with 0xff
|
|
|
|
if i.DefaultPixelValue() != 0 {
|
|
|
|
p.Bitmap.SetDefaultPixel()
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, h := range p.Segments {
|
|
|
|
switch h.Type {
|
|
|
|
case 6, 7, 22, 23, 38, 39, 42, 43:
|
|
|
|
common.Log.Trace("Getting Segment: %d", h.SegmentNumber)
|
|
|
|
s, err := h.GetSegmentData()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
r, ok := s.(segments.Regioner)
|
|
|
|
if !ok {
|
|
|
|
common.Log.Debug("Segment: %T is not a Regioner", s)
|
|
|
|
return errors.New("invalid jbig2 segment type - not a Regioner")
|
|
|
|
}
|
|
|
|
|
|
|
|
regionBitmap, err := r.GetRegionBitmap()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.fitsPage(i, regionBitmap) {
|
|
|
|
p.Bitmap = regionBitmap
|
|
|
|
} else {
|
|
|
|
regionInfo := r.GetRegionInfo()
|
|
|
|
op := p.getCombinationOperator(i, regionInfo.CombinaionOperator)
|
2019-08-29 21:12:18 +02:00
|
|
|
err = bitmap.Blit(regionBitmap, p.Bitmap, int(regionInfo.XLocation), int(regionInfo.YLocation), op)
|
2019-07-14 23:18:40 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) createStripedPage(i *segments.PageInformationSegment) error {
|
|
|
|
pageStripes, err := p.collectPageStripes()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var startLine int
|
|
|
|
|
|
|
|
for _, sd := range pageStripes {
|
|
|
|
if eos, ok := sd.(*segments.EndOfStripe); ok {
|
|
|
|
startLine = eos.LineNumber() + 1
|
|
|
|
} else {
|
|
|
|
r := sd.(segments.Regioner)
|
|
|
|
regionInfo := r.GetRegionInfo()
|
|
|
|
op := p.getCombinationOperator(i, regionInfo.CombinaionOperator)
|
|
|
|
regionBitmap, err := r.GetRegionBitmap()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-08-29 21:12:18 +02:00
|
|
|
err = bitmap.Blit(regionBitmap, p.Bitmap, int(regionInfo.XLocation), startLine, op)
|
2019-07-14 23:18:40 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) collectPageStripes() ([]segments.Segmenter, error) {
|
|
|
|
var (
|
|
|
|
stripes []segments.Segmenter
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
for _, h := range p.Segments {
|
|
|
|
switch h.Type {
|
|
|
|
case 6, 7, 22, 23, 38, 39, 42, 43:
|
|
|
|
var s segments.Segmenter
|
|
|
|
s, err = h.GetSegmentData()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
stripes = append(stripes, s)
|
|
|
|
case 50:
|
|
|
|
var s segments.Segmenter
|
|
|
|
s, err = h.GetSegmentData()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
eos, ok := s.(*segments.EndOfStripe)
|
|
|
|
if !ok {
|
|
|
|
err = errors.New("segment EndOfStripe is of invalid type")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
stripes = append(stripes, eos)
|
|
|
|
p.FinalHeight = eos.LineNumber()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return stripes, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) clearSegmentData() {
|
|
|
|
for i := range p.Segments {
|
|
|
|
p.Segments[i].CleanSegmentData()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) clearPageData() {
|
|
|
|
p.Bitmap = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// countRegions counts the region segments in the Page.
|
|
|
|
func (p *Page) countRegions() int {
|
|
|
|
var regionCount int
|
|
|
|
|
|
|
|
for _, h := range p.Segments {
|
|
|
|
switch h.Type {
|
|
|
|
case 6, 7, 22, 23, 38, 39, 42, 43:
|
|
|
|
regionCount++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return regionCount
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) fitsPage(i *segments.PageInformationSegment, regionBitmap *bitmap.Bitmap) bool {
|
|
|
|
return p.countRegions() == 1 &&
|
|
|
|
i.DefaultPixelValue() == 0 &&
|
|
|
|
i.PageBMWidth == regionBitmap.Width &&
|
|
|
|
i.PageBMHeight == regionBitmap.Height
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) getCombinationOperator(i *segments.PageInformationSegment, newOperator bitmap.CombinationOperator) bitmap.CombinationOperator {
|
|
|
|
if i.CombinationOperatorOverrideAllowed() {
|
|
|
|
return newOperator
|
|
|
|
}
|
|
|
|
return i.CombinationOperator()
|
|
|
|
}
|
|
|
|
|
|
|
|
// getPageInformationSegment returns the associated page information segment.
|
|
|
|
func (p *Page) getPageInformationSegment() *segments.Header {
|
|
|
|
for _, s := range p.Segments {
|
|
|
|
if s.Type == segments.TPageInformation {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
}
|
|
|
|
common.Log.Debug("Page information segment not found for page: %s.", p)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) getHeight() (int, error) {
|
|
|
|
if p.FinalHeight == 0 {
|
|
|
|
h := p.getPageInformationSegment()
|
|
|
|
if h == nil {
|
|
|
|
return 0, errors.New("nil page information")
|
|
|
|
}
|
|
|
|
|
|
|
|
s, err := h.GetSegmentData()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pi, ok := s.(*segments.PageInformationSegment)
|
|
|
|
if !ok {
|
|
|
|
return 0, errors.New("page information segment is of invalid type")
|
|
|
|
}
|
|
|
|
|
2019-08-29 21:12:18 +02:00
|
|
|
if pi.PageBMHeight == math.MaxInt32 {
|
2019-07-14 23:18:40 +02:00
|
|
|
_, err = p.GetBitmap()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
p.FinalHeight = pi.PageBMHeight
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p.FinalHeight, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) getWidth() (int, error) {
|
|
|
|
if p.FinalWidth == 0 {
|
|
|
|
h := p.getPageInformationSegment()
|
|
|
|
if h == nil {
|
|
|
|
return 0, errors.New("nil page information")
|
|
|
|
}
|
|
|
|
|
|
|
|
s, err := h.GetSegmentData()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pi, ok := s.(*segments.PageInformationSegment)
|
|
|
|
if !ok {
|
|
|
|
return 0, errors.New("page information segment is of invalid type")
|
|
|
|
}
|
|
|
|
|
|
|
|
p.FinalWidth = pi.PageBMWidth
|
|
|
|
}
|
|
|
|
return p.FinalWidth, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) getResolutionX() (int, error) {
|
|
|
|
if p.ResolutionX == 0 {
|
|
|
|
h := p.getPageInformationSegment()
|
|
|
|
if h == nil {
|
|
|
|
return 0, errors.New("nil page information")
|
|
|
|
}
|
|
|
|
|
|
|
|
s, err := h.GetSegmentData()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pi, ok := s.(*segments.PageInformationSegment)
|
|
|
|
if !ok {
|
|
|
|
return 0, errors.New("page information segment is of invalid type")
|
|
|
|
}
|
|
|
|
|
|
|
|
p.ResolutionX = pi.ResolutionX
|
|
|
|
}
|
|
|
|
return p.ResolutionX, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Page) getResolutionY() (int, error) {
|
|
|
|
if p.ResolutionY == 0 {
|
|
|
|
h := p.getPageInformationSegment()
|
|
|
|
if h == nil {
|
|
|
|
return 0, errors.New("nil page information")
|
|
|
|
}
|
|
|
|
|
|
|
|
s, err := h.GetSegmentData()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pi, ok := s.(*segments.PageInformationSegment)
|
|
|
|
if !ok {
|
|
|
|
return 0, errors.New("page information segment is of invalid type")
|
|
|
|
}
|
|
|
|
|
|
|
|
p.ResolutionY = pi.ResolutionY
|
|
|
|
}
|
|
|
|
return p.ResolutionY, nil
|
|
|
|
}
|