Skip to content

Commit

Permalink
Merge pull request unidoc#295 from gunnsth/release/v3.6.0
Browse files Browse the repository at this point in the history
Prepare unipdf release v3.6.0
  • Loading branch information
gunnsth authored Apr 7, 2020
2 parents ed30fe6 + dadddb4 commit 0b89bf0
Show file tree
Hide file tree
Showing 143 changed files with 24,691 additions and 2,553 deletions.
22 changes: 21 additions & 1 deletion ACKNOWLEDGEMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```

* [Apache Java PDFBox JBIG2 Decoder](https://github.com/apache/pdfbox-jbig2), Apache License 2.0.
- Used as a base for the JBIG2 image decoder.
In order to achieve full support for the JBIG2 Decoder, it was necessary to implement all possible decoding
combinations defined in the JBIG2 standard, aka ITU T.88 and ISO/IEC 14492.
With a lack of Golang JBIG2 Open Source package, we’ve decided that it would be best to base our own implementation
on some solid and reliable library.
The Apache PDFBox JBIG2 library fulfilled all our requirements. It has a really good quality of the code along with
the detailed comments on each function and class. It also implemented MMR, Huffman tables and arithmetic
decompressors along with all JBIG2 segments.

* [AGL JBIG2 Encoder](https://github.com/agl/jbig2enc), Apache License 2.0.
The complexity and lack of comprehensive documentation for the JBIG2 encoding process, lead us to look at the
AGL JBIG2 Encoder library. At the moment of implementing our encoder it was the only Open Source JBIG2 encoder.
It’s a C++ based library that implements both lossless and lossy encoding methods, where most of the image
operations are done using DanBloomberg Leptonica library.

The core encoding processes in the UniPDF JBIG2 Encoder were based on that well documented and solid library


* [DanBloomberg Leptonica](https://github.com/DanBloomberg/leptonica), The 2-Clause BSD License,
DanBloomberg Leptonica is an amazing C/C++ Open Source library. It provides raster operations, binary expansion and
reduction, JBIG2 component creators, correlation scoring and a lot more perfectly commented image operation functions.
That library was used as a very solid base for our image operation algorithms used by the JBIG2 Encoder.
Binary file added checkerboard-squares-black-white.jb2
Binary file not shown.
99 changes: 94 additions & 5 deletions common/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ package common

import (
"fmt"
"io"
"os"
"path/filepath"
"runtime"
)

// Logger is the interface used for logging in the unipdf package.
type Logger interface {
Error(format string, args ...interface{})
Warning(format string, args ...interface{})
Expand Down Expand Up @@ -57,6 +59,8 @@ func (DummyLogger) IsLogLevel(level LogLevel) bool {
// LogLevel is the verbosity level for logging.
type LogLevel int

// Defines log level enum where the most important logs have the lowest values.
// I.e. level error = 0 and level trace = 5
const (
LogLevelTrace LogLevel = 5
LogLevelDebug LogLevel = 4
Expand All @@ -66,14 +70,14 @@ const (
LogLevelError LogLevel = 0
)

// ConsoleLogger is a logger that writes logs to the 'os.Stdout'
type ConsoleLogger struct {
LogLevel LogLevel
}

// NewConsoleLogger creates new console logger.
func NewConsoleLogger(logLevel LogLevel) *ConsoleLogger {
logger := ConsoleLogger{}
logger.LogLevel = logLevel
return &logger
return &ConsoleLogger{LogLevel: logLevel}
}

// IsLogLevel returns true if log level is greater or equal than `level`.
Expand All @@ -82,56 +86,141 @@ func (l ConsoleLogger) IsLogLevel(level LogLevel) bool {
return l.LogLevel >= level
}

// Error logs error message.
func (l ConsoleLogger) Error(format string, args ...interface{}) {
if l.LogLevel >= LogLevelError {
prefix := "[ERROR] "
l.output(os.Stdout, prefix, format, args...)
}
}

// Warning logs warning message.
func (l ConsoleLogger) Warning(format string, args ...interface{}) {
if l.LogLevel >= LogLevelWarning {
prefix := "[WARNING] "
l.output(os.Stdout, prefix, format, args...)
}
}

// Notice logs notice message.
func (l ConsoleLogger) Notice(format string, args ...interface{}) {
if l.LogLevel >= LogLevelNotice {
prefix := "[NOTICE] "
l.output(os.Stdout, prefix, format, args...)
}
}

// Info logs info message.
func (l ConsoleLogger) Info(format string, args ...interface{}) {
if l.LogLevel >= LogLevelInfo {
prefix := "[INFO] "
l.output(os.Stdout, prefix, format, args...)
}
}

// Debug logs debug message.
func (l ConsoleLogger) Debug(format string, args ...interface{}) {
if l.LogLevel >= LogLevelDebug {
prefix := "[DEBUG] "
l.output(os.Stdout, prefix, format, args...)
}
}

// Trace logs trace message.
func (l ConsoleLogger) Trace(format string, args ...interface{}) {
if l.LogLevel >= LogLevelTrace {
prefix := "[TRACE] "
l.output(os.Stdout, prefix, format, args...)
}
}

// output writes `format`, `args` log message prefixed by the source file name, line and `prefix`
func (l ConsoleLogger) output(f io.Writer, prefix string, format string, args ...interface{}) {
logToWriter(f, prefix, format, args...)
}

var Log Logger = DummyLogger{}

// SetLogger sets 'logger' to be used by the unidoc unipdf library.
func SetLogger(logger Logger) {
Log = logger
}

// output writes `format`, `args` log message prefixed by the source file name, line and `prefix`
func (l ConsoleLogger) output(f *os.File, prefix string, format string, args ...interface{}) {
// WriterLogger is the logger that writes data to the Output writer
type WriterLogger struct {
LogLevel LogLevel
Output io.Writer
}

// NewWriterLogger creates new 'writer' logger.
func NewWriterLogger(logLevel LogLevel, writer io.Writer) *WriterLogger {
logger := WriterLogger{
Output: writer,
LogLevel: logLevel,
}
return &logger
}

// IsLogLevel returns true if log level is greater or equal than `level`.
// Can be used to avoid resource intensive calls to loggers.
func (l WriterLogger) IsLogLevel(level LogLevel) bool {
return l.LogLevel >= level
}

// Error logs error message.
func (l WriterLogger) Error(format string, args ...interface{}) {
if l.LogLevel >= LogLevelError {
prefix := "[ERROR] "
l.logToWriter(l.Output, prefix, format, args...)
}
}

// Warning logs warning message.
func (l WriterLogger) Warning(format string, args ...interface{}) {
if l.LogLevel >= LogLevelWarning {
prefix := "[WARNING] "
l.logToWriter(l.Output, prefix, format, args...)
}
}

// Notice logs notice message.
func (l WriterLogger) Notice(format string, args ...interface{}) {
if l.LogLevel >= LogLevelNotice {
prefix := "[NOTICE] "
l.logToWriter(l.Output, prefix, format, args...)
}
}

// Info logs info message.
func (l WriterLogger) Info(format string, args ...interface{}) {
if l.LogLevel >= LogLevelInfo {
prefix := "[INFO] "
l.logToWriter(l.Output, prefix, format, args...)
}
}

// Debug logs debug message.
func (l WriterLogger) Debug(format string, args ...interface{}) {
if l.LogLevel >= LogLevelDebug {
prefix := "[DEBUG] "
l.logToWriter(l.Output, prefix, format, args...)
}
}

// Trace logs trace message.
func (l WriterLogger) Trace(format string, args ...interface{}) {
if l.LogLevel >= LogLevelTrace {
prefix := "[TRACE] "
l.logToWriter(l.Output, prefix, format, args...)
}
}

// logToWriter writes `format`, `args` log message prefixed by the source file name, line and `prefix`
func (l WriterLogger) logToWriter(f io.Writer, prefix string, format string, args ...interface{}) {
logToWriter(f, prefix, format, args)
}

func logToWriter(f io.Writer, prefix string, format string, args ...interface{}) {
_, file, line, ok := runtime.Caller(2)
if !ok {
file = "???"
Expand Down
6 changes: 3 additions & 3 deletions common/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
)

const releaseYear = 2020
const releaseMonth = 3
const releaseDay = 8
const releaseMonth = 4
const releaseDay = 7
const releaseHour = 23
const releaseMin = 40

// Version holds version information, when bumping this make sure to bump the released at stamp also.
const Version = "3.5.0"
const Version = "3.6.0"

var ReleasedAt = time.Date(releaseYear, releaseMonth, releaseDay, releaseHour, releaseMin, 0, 0, time.UTC)
1 change: 1 addition & 0 deletions contentstream/inline-image.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func NewInlineImageFromImage(img model.Image, encoder core.StreamEncoder) (*Cont
if encoder == nil {
encoder = core.NewRawEncoder()
}
encoder.UpdateParams(img.GetParamsDict())

inlineImage := ContentStreamInlineImage{}
if img.ColorComponents == 1 {
Expand Down
4 changes: 4 additions & 0 deletions contentstream/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ func (proc *ContentStreamProcessor) Process(resources *model.PdfPageResources) e
case "q":
proc.graphicsStack.Push(proc.graphicsState)
case "Q":
if len(proc.graphicsStack) == 0 {
common.Log.Debug("WARN: invalid `Q` operator. Graphics state stack is empty. Skipping.")
continue
}
proc.graphicsState = proc.graphicsStack.Pop()

// Color operations (Table 74 p. 179)
Expand Down
Loading

0 comments on commit 0b89bf0

Please sign in to comment.