mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2025-04-12 14:04:08 +00:00
Merge branch 'exif' into 'develop'
Strip GPS data without exiftool See merge request pleroma/pleroma!4178
This commit is contained in:
commit
49579d7d65
2 changed files with 102 additions and 10 deletions
1
changelog.d/4178-strip-gps-without-exiftool.change
Normal file
1
changelog.d/4178-strip-gps-without-exiftool.change
Normal file
|
@ -0,0 +1 @@
|
|||
Strip GPS data of JPGs and PNGs without exiftool.
|
|
@ -15,18 +15,109 @@ defmodule Pleroma.Upload.Filter.Exiftool.StripLocation do
|
|||
def filter(%Pleroma.Upload{content_type: "image/svg" <> _}), do: {:ok, :noop}
|
||||
|
||||
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
|
||||
try do
|
||||
case System.cmd("exiftool", ["-overwrite_original", "-gps:all=", "-png:all=", file],
|
||||
parallelism: true
|
||||
) do
|
||||
{_response, 0} -> {:ok, :filtered}
|
||||
{error, 1} -> {:error, error}
|
||||
end
|
||||
rescue
|
||||
e in ErlangError ->
|
||||
{:error, "#{__MODULE__}: #{inspect(e)}"}
|
||||
case ExifGpsStripper.strip_gps_data(file, file) do
|
||||
:ok -> {:ok, :filtered}
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(_), do: {:ok, :noop}
|
||||
end
|
||||
|
||||
defmodule ExifGpsStripper do
|
||||
@exif_header <<0xFF, 0xE1>>
|
||||
@gps_ifd_tag 0x8825
|
||||
|
||||
def strip_gps_data(input_path, output_path) do
|
||||
case File.read(input_path) do
|
||||
{:ok, data} ->
|
||||
case process_file(data) do
|
||||
{:ok, stripped_data} -> File.write(output_path, stripped_data)
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, "Failed to read file: #{reason}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp process_file(<<0x89, "PNG", 0x0D, 0x0A, 0x1A, 0x0A, rest::binary>>) do
|
||||
# PNG file
|
||||
{:ok, <<0x89, "PNG", 0x0D, 0x0A, 0x1A, 0x0A>> <> strip_png_gps(rest)}
|
||||
end
|
||||
|
||||
defp process_file(<<0xFF, 0xD8, rest::binary>>) do
|
||||
# JPEG file
|
||||
{:ok, <<0xFF, 0xD8>> <> strip_jpeg_gps(rest)}
|
||||
end
|
||||
|
||||
defp process_file(_), do: {:error, "Unsupported file format"}
|
||||
|
||||
defp strip_png_gps(data) do
|
||||
strip_png_chunks(data, [])
|
||||
end
|
||||
|
||||
defp strip_png_chunks(<<>>, acc), do: IO.iodata_to_binary(Enum.reverse(acc))
|
||||
|
||||
defp strip_png_chunks(
|
||||
<<chunk_size::32, chunk_type::binary-size(4), chunk_data::binary-size(chunk_size),
|
||||
crc::32, rest::binary>>,
|
||||
acc
|
||||
) do
|
||||
case chunk_type do
|
||||
"eXIf" ->
|
||||
strip_png_chunks(rest, acc)
|
||||
|
||||
"tEXt" ->
|
||||
strip_png_chunks(rest, acc)
|
||||
|
||||
_ ->
|
||||
chunk =
|
||||
<<chunk_size::32, chunk_type::binary-size(4), chunk_data::binary-size(chunk_size),
|
||||
crc::32>>
|
||||
|
||||
strip_png_chunks(rest, [chunk | acc])
|
||||
end
|
||||
end
|
||||
|
||||
defp strip_jpeg_gps(data) do
|
||||
case :binary.match(data, @exif_header) do
|
||||
:nomatch ->
|
||||
data
|
||||
|
||||
{pos, _} ->
|
||||
<<before::binary-size(pos), @exif_header, size::16, exif_data::binary-size(size - 2),
|
||||
rest::binary>> = data
|
||||
|
||||
stripped_exif = strip_gps_from_exif(exif_data)
|
||||
new_size = byte_size(stripped_exif) + 2
|
||||
before <> @exif_header <> <<new_size::16>> <> stripped_exif <> rest
|
||||
end
|
||||
end
|
||||
|
||||
defp strip_gps_from_exif(<<"Exif", 0, 0, tiff_header::binary-size(8), ifd_data::binary>>) do
|
||||
{stripped_ifd, _} = strip_gps_from_ifd(ifd_data)
|
||||
<<"Exif", 0, 0, tiff_header::binary-size(8), stripped_ifd::binary>>
|
||||
end
|
||||
|
||||
defp strip_gps_from_ifd(<<count::16-little, rest::binary>>) do
|
||||
{entries, remaining} = strip_gps_from_entries(rest, count, [])
|
||||
{<<count::16-little>> <> IO.iodata_to_binary(entries), remaining}
|
||||
end
|
||||
|
||||
defp strip_gps_from_entries(data, 0, acc), do: {Enum.reverse(acc), data}
|
||||
|
||||
defp strip_gps_from_entries(
|
||||
<<tag::16-little, type::16-little, count::32-little, value::32-little, rest::binary>>,
|
||||
entries_left,
|
||||
acc
|
||||
) do
|
||||
entry = <<tag::16-little, type::16-little, count::32-little, value::32-little>>
|
||||
|
||||
if tag == @gps_ifd_tag do
|
||||
strip_gps_from_entries(rest, entries_left - 1, acc)
|
||||
else
|
||||
strip_gps_from_entries(rest, entries_left - 1, [entry | acc])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue