Current section
Files
Jump to
Current section
Files
lib/krita_ex.ex
defmodule KritaEx do
@moduledoc """
Provides functions to extract embedded images from Krita .kra files
"""
@doc """
Extracts the embedded png to a specified location
## Parameters
- kra: Path to .kra file or binary content (use of `raw` is required to use binary)
- out_path: Output filepath
- options:
- raw: expect binary input (default `false`)
- create_dirs: create output directories (default `true`)
Returns `:ok` or `{:error, reason}`.
## Examples
iex(1)> KritaEx.extract_png("test/files/test.kra", "tmp/output.png")
:ok
iex(2)> KritaEx.extract_png("idontexist.kra", "tmp/output.png")
{:error, {:failed_read, :enoent}}
"""
def extract_png(kra, out_path, options \\ []) do
defaults = [create_dirs: true, raw: false]
%{create_dirs: create_dirs, raw: raw} =
Keyword.merge(defaults, options) |> Enum.into(%{})
extract_png(kra, out_path, create_dirs, raw)
end
defp extract_png(kra, out_path, create_dirs, raw)
defp extract_png(kra, out_path, create_dirs, false) when is_binary(kra),
do: extract_png(:binary.bin_to_list(kra), out_path, create_dirs, false)
defp extract_png(kra, out_path, create_dirs, raw) do
with {:ok, binary} <- read_png(kra, raw: raw),
:ok <- create_file_dir(out_path, create_dirs) do
write(out_path, binary)
end
end
@doc """
Aliased to `read_png/2` with `raw: false`
"""
def read_png(kra), do: read_png(kra, raw: false)
@doc """
Reads the embedded png in binary format
## Parameters
- kra: Path to .kra file or file contents (use of `raw` is required to use binary)
- options:
- raw: expect binary input (default: `false`, see `read_png/1`)
## Examples
KritaEx.read_png("test/files/test.kra")
{:ok,
<<137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 64, 0,
0, 0, 64, 8, 6, 0, 0, 0, 170, 105, 113, 222, 0, 0, 0, 9, 112, 72, 89, 115, 0,
0, 46, 35, 0, 0, 46, ...>>}
"""
def read_png(kra, raw: false) when is_binary(kra),
do: read_png(:binary.bin_to_list(kra), raw: false)
def read_png(kra, raw: _) do
with {:ok, files} <- list_files(kra),
:ok <- check_merged_file_exists(files) do
extract_merged_file(kra)
end
end
@doc """
Aliased to `valid_kra?/2` with `raw: false`
"""
def valid_kra?(kra), do: valid_kra?(kra, raw: false)
@doc """
Verifies that the supplied .kra is valid
## Parameters
- kra: Path to .kra file or file contents (use of `raw` is required to use binary)
- options:
- raw: expect binary input (default: `false`, see `valid_kra?/1`)
Returns `true` or `false`
## Examples
iex(1)> KritaEx.valid_kra?("test/files/test.kra")
true
iex(2)> KritaEx.valid_kra?("test/files/invalid.kra")
false
"""
def valid_kra?(kra, raw: false) when is_binary(kra),
do: valid_kra?(:binary.bin_to_list(kra), raw: false)
def valid_kra?(kra, raw: _) do
result =
with {:ok, files} <- list_files(kra) do
check_merged_file_exists(files)
end
case result do
:ok -> true
_ -> false
end
end
defp list_files(kra_path) do
case :zip.list_dir(kra_path) do
{:ok, _} = resp -> resp
{:error, :bad_eocd} -> {:error, :invalid_kra}
{:error, :einval} -> {:error, :invalid_kra}
{:error, errcode} -> {:error, {:failed_read, errcode}}
end
end
defp check_merged_file_exists(files) do
merged_image_file =
files
|> Enum.find(
&case &1 do
{:zip_file, ~c"mergedimage.png", _, _, _, _} -> true
_ -> false
end
)
case merged_image_file do
nil -> {:error, :invalid_kra}
_ -> :ok
end
end
defp extract_merged_file(kra_path) do
result =
:zip.unzip(
kra_path,
[
{:file_list, [~c"mergedimage.png"]},
:memory
]
)
case result do
{:ok, [{_filename, binary}]} -> {:ok, binary}
{:error, errcode} -> {:error, {:failed_extract, errcode}}
end
end
defp create_file_dir(_, false), do: :ok
defp create_file_dir(filepath, true) do
dir = Path.dirname(filepath)
case File.mkdir_p(dir) do
{:error, errcode} -> {:error, {:failed_mkdir, errcode}}
:ok -> :ok
end
end
defp write(out_path, binary) do
case File.write(out_path, binary) do
:ok -> :ok
{:error, errcode} -> {:error, {:failed_write, errcode}}
end
end
end