<commit_before>using Compat

# Cairo backend for compose
import Cairo

using Cairo: CairoContext, CairoSurface, CairoARGBSurface,
             CairoEPSSurface, CairoPDFSurface, CairoSVGSurface,
             CairoImageSurface

abstract ImageBackend
abstract PNGBackend <: ImageBackend

abstract VectorImageBackend <: ImageBackend
abstract SVGBackend <: VectorImageBackend
abstract PDFBackend <: VectorImageBackend
abstract PSBackend  <: VectorImageBackend
abstract CairoBackend <: VectorImageBackend

type ImagePropertyState
    stroke::RGBA{Float64}
    fill::RGBA{Float64}
    stroke_dash::Array{Float64,1}
    stroke_linecap::LineCap
    stroke_linejoin::LineJoin
    visible::Bool
    linewidth::AbsoluteLength
    fontsize::AbsoluteLength
    font::AbstractString
    clip::Nullable{ClipPrimitive}
end

type ImagePropertyFrame
    # Vector properties in this frame.
    vector_properties::Dict{Type, Property}

    # True if this property frame has scalar properties. Scalar properties are
    # emitted as a group (<g> tag) that must be closed when the frame is popped.
    has_scalar_properties::Bool

    function ImagePropertyFrame()
        return new(Dict{Type, Property}(), false)
    end
end

type Image{B <: ImageBackend} <: Backend
    out::IO
    surface::CairoSurface
    ctx::CairoContext

    width::Float64
    height::Float64

    # Current state
    stroke::RGBA{Float64}
    fill::RGBA{Float64}
    stroke_dash::Array{Float64,1}
    stroke_linecap::LineCap
    stroke_linejoin::LineJoin
    visible::Bool
    linewidth::AbsoluteLength
    fontsize::AbsoluteLength
    font::AbstractString
    clip::Nullable{ClipPrimitive}

    # Keep track of property
    state_stack::Vector{ImagePropertyState}
    property_stack::Vector{ImagePropertyFrame}
    vector_properties::Dict{Type, Nullable{Property}}

    # Close the surface when finished
    owns_surface::Bool

    # Backend is responsible for opening/closing the file
    ownedfile::Bool

    # Filename when ownedfile is true
    filename::@compat(Union{AbstractString, (@compat Void)})

    # True when finish has been called and no more drawing should occur
    finished::Bool

    # Emit on finish
    emit_on_finish::Bool

    # Points (or pixels for PNG) per mm
    ppmm::Float64

    # For use with the t/T and s/S commands in SVG-style paths
    last_ctrl1_point::Nullable{AbsoluteVec2}
    last_ctrl2_point::Nullable{AbsoluteVec2}

    function Image(surface::CairoSurface, ctx::CairoContext, out::IO)
        img = new()
        img.out = out
        img.width = 0
        img.height = 0
        img.surface = surface
        img.ctx = ctx

        img.stroke = default_stroke_color == nothing ?
                        RGBA{Float64}(0, 0, 0, 0) : convert(RGBA{Float64}, default_stroke_color)
        img.fill   = default_fill_color == nothing ?
                        RGBA{Float64}(0, 0, 0, 0) : convert(RGBA{Float64}, default_fill_color)
        img.stroke_dash = []
        img.stroke_linecap = LineCapButt()
        img.stroke_linejoin = LineJoinMiter()
        img.visible = true
        img.linewidth = default_line_width
        img.fontsize = default_font_size
        img.font = default_font_family
        img.clip = Nullable{ClipPrimitive}()

        img.state_stack = Array(ImagePropertyState, 0)
        img.property_stack = Array(ImagePropertyFrame, 0)
        img.vector_properties = Dict{Type, Nullable{Property}}()
        img.owns_surface = false
        img.ownedfile = false
        img.filename = nothing
        img.finished = false
        img.emit_on_finish = false
        img.ppmm = 72 / 25.4
        img.last_ctrl1_point = Nullable{AbsoluteVec2}()
        img.last_ctrl2_point = Nullable{AbsoluteVec2}()
        img
    end

    function Image(surface::CairoSurface, ctx::CairoContext)
        Image{B}(surface, ctx, IOBuffer())
    end

    Image(surface::CairoSurface) = Image{B}(surface, CairoContext(surface))


    function Image(out::IO,
                   width::MeasureOrNumber,
                   height::MeasureOrNumber,
                   emit_on_finish::Bool=true;
                   dpi = (B == PNGBackend ? 96 : 72))

        width = size_measure(width)
        height = size_measure(height)

        if !isa(width, AbsoluteLength) || !isa(height, AbsoluteLength)
            error("Image size must be specificed in absolute units.")
        end

        ppmm = dpi / 25.4
        width = width.value * ppmm
        height = height.value * ppmm

        surface = newsurface(B, out, width, height)
        img = Image{B}(surface, CairoContext(surface), out)
        img.width = width
        img.height = height
        img.owns_surface = true
        img.emit_on_finish = emit_on_finish
        img.ppmm = ppmm
        img
    end

    function Image(filename::AbstractString,
                   width::MeasureOrNumber,
                   height::MeasureOrNumber;
                   dpi = (B == PNGBackend ? 96 : 72))
        img = Image{B}(open(filename, "w"), width, height, dpi = dpi)
        img.ownedfile = true
        img.filename = filename
        img
    end

    function Image(width::MeasureOrNumber,
                   height::MeasureOrNumber,
                   emit_on_finish::Bool=true)
        img = Image{B}(IOBuffer(), width, height, emit_on_finish)
        img
    end

    function Image(c::CairoSurface)
        img = Image{B}(c,CairoContext(c))
        img
    end
end


typealias PNG Image{PNGBackend}
typealias PDF Image{PDFBackend}
typealias PS  Image{PSBackend}
typealias CAIROSURFACE  Image{CairoBackend}


function canbatch(img::Image)
    for vp in values(img.vector_properties)
        if !isnull(vp)
            return false
        end<commit_msg>Added DPI keyword argument for Image constructor when no output buffer or filename arguments are provided.<commit_after>using Compat

# Cairo backend for compose
import Cairo

using Cairo: CairoContext, CairoSurface, CairoARGBSurface,
             CairoEPSSurface, CairoPDFSurface, CairoSVGSurface,
             CairoImageSurface

abstract ImageBackend
abstract PNGBackend <: ImageBackend

abstract VectorImageBackend <: ImageBackend
abstract SVGBackend <: VectorImageBackend
abstract PDFBackend <: VectorImageBackend
abstract PSBackend  <: VectorImageBackend
abstract CairoBackend <: VectorImageBackend

type ImagePropertyState
    stroke::RGBA{Float64}
    fill::RGBA{Float64}
    stroke_dash::Array{Float64,1}
    stroke_linecap::LineCap
    stroke_linejoin::LineJoin
    visible::Bool
    linewidth::AbsoluteLength
    fontsize::AbsoluteLength
    font::AbstractString
    clip::Nullable{ClipPrimitive}
end

type ImagePropertyFrame
    # Vector properties in this frame.
    vector_properties::Dict{Type, Property}

    # True if this property frame has scalar properties. Scalar properties are
    # emitted as a group (<g> tag) that must be closed when the frame is popped.
    has_scalar_properties::Bool

    function ImagePropertyFrame()
        return new(Dict{Type, Property}(), false)
    end
end

type Image{B <: ImageBackend} <: Backend
    out::IO
    surface::CairoSurface
    ctx::CairoContext

    width::Float64
    height::Float64

    # Current state
    stroke::RGBA{Float64}
    fill::RGBA{Float64}
    stroke_dash::Array{Float64,1}
    stroke_linecap::LineCap
    stroke_linejoin::LineJoin
    visible::Bool
    linewidth::AbsoluteLength
    fontsize::AbsoluteLength
    font::AbstractString
    clip::Nullable{ClipPrimitive}

    # Keep track of property
    state_stack::Vector{ImagePropertyState}
    property_stack::Vector{ImagePropertyFrame}
    vector_properties::Dict{Type, Nullable{Property}}

    # Close the surface when finished
    owns_surface::Bool

    # Backend is responsible for opening/closing the file
    ownedfile::Bool

    # Filename when ownedfile is true
    filename::@compat(Union{AbstractString, (@compat Void)})

    # True when finish has been called and no more drawing should occur
    finished::Bool

    # Emit on finish
    emit_on_finish::Bool

    # Points (or pixels for PNG) per mm
    ppmm::Float64

    # For use with the t/T and s/S commands in SVG-style paths
    last_ctrl1_point::Nullable{AbsoluteVec2}
    last_ctrl2_point::Nullable{AbsoluteVec2}

    function Image(surface::CairoSurface, ctx::CairoContext, out::IO)
        img = new()
        img.out = out
        img.width = 0
        img.height = 0
        img.surface = surface
        img.ctx = ctx

        img.stroke = default_stroke_color == nothing ?
                        RGBA{Float64}(0, 0, 0, 0) : convert(RGBA{Float64}, default_stroke_color)
        img.fill   = default_fill_color == nothing ?
                        RGBA{Float64}(0, 0, 0, 0) : convert(RGBA{Float64}, default_fill_color)
        img.stroke_dash = []
        img.stroke_linecap = LineCapButt()
        img.stroke_linejoin = LineJoinMiter()
        img.visible = true
        img.linewidth = default_line_width
        img.fontsize = default_font_size
        img.font = default_font_family
        img.clip = Nullable{ClipPrimitive}()

        img.state_stack = Array(ImagePropertyState, 0)
        img.property_stack = Array(ImagePropertyFrame, 0)
        img.vector_properties = Dict{Type, Nullable{Property}}()
        img.owns_surface = false
        img.ownedfile = false
        img.filename = nothing
        img.finished = false
        img.emit_on_finish = false
        img.ppmm = 72 / 25.4
        img.last_ctrl1_point = Nullable{AbsoluteVec2}()
        img.last_ctrl2_point = Nullable{AbsoluteVec2}()
        img
    end

    function Image(surface::CairoSurface, ctx::CairoContext)
        Image{B}(surface, ctx, IOBuffer())
    end

    Image(surface::CairoSurface) = Image{B}(surface, CairoContext(surface))


    function Image(out::IO,
                   width::MeasureOrNumber,
                   height::MeasureOrNumber,
                   emit_on_finish::Bool=true;
                   dpi = (B == PNGBackend ? 96 : 72))

        width = size_measure(width)
        height = size_measure(height)

        if !isa(width, AbsoluteLength) || !isa(height, AbsoluteLength)
            error("Image size must be specificed in absolute units.")
        end

        ppmm = dpi / 25.4
        width = width.value * ppmm
        height = height.value * ppmm

        surface = newsurface(B, out, width, height)
        img = Image{B}(surface, CairoContext(surface), out)
        img.width = width
        img.height = height
        img.owns_surface = true
        img.emit_on_finish = emit_on_finish
        img.ppmm = ppmm
        img
    end

    function Image(filename::AbstractString,
                   width::MeasureOrNumber,
                   height::MeasureOrNumber;
                   dpi = (B == PNGBackend ? 96 : 72))
        img = Image{B}(open(filename, "w"), width, height, dpi = dpi)
        img.ownedfile = true
        img.filename = filename
        img
    end

    function Image(width::MeasureOrNumber,
                   height::MeasureOrNumber,
                   emit_on_finish::Bool=true;
                   dpi = (B == PNGBackend ? 96 : 72))
        img = Image{B}(IOBuffer(), width, height, emit_on_finish, dpi = dpi)
        img
    end

    function Image(c::CairoSurface)
        img = Image{B}(c,CairoContext(c))
        img
    end
end


typealias PNG Image{PNGBackend}
typealias PDF Image{PDFBackend}
typealias PS  Image{PSBackend}
typealias CAIROSURFACE  Image{CairoBackend}


function canbatch(img::Image)
    for vp in values(img.vector_properties)
        if !isnull(vp)
            return false
        end
{'fraction_non_alphanumeric': 0.08807260155574763, 'fraction_numerical': 0.011408815903197925, 'mean_word_length': 2.699168797953964, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6500695', 'n_tokens_mistral': 3458, 'n_tokens_neox': 3297, 'n_words': 803}