Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Images - oneCellAnchor #135

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ elixlsx-*.tar

# Misc.
*.swp

# Elixir LS cache
/.elixir_ls/
2 changes: 1 addition & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
import Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
Expand Down
80 changes: 74 additions & 6 deletions example.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ require Elixlsx

alias Elixlsx.{Sheet, Workbook}

sheet1 = Sheet.with_name("First")
sheet1 =
Sheet.with_name("First")
# Set cell B2 to the string "Hi". :)
|> Sheet.set_cell("B2", "Hi")
# Optionally, set font properties:
Expand Down Expand Up @@ -48,11 +49,15 @@ sheet1 = Sheet.with_name("First")
|> Sheet.set_cell("A3", "cow")
|> Sheet.add_data_validations("A1", "A10", ["dog", "cat", "cow"])
# within same sheet
|> Sheet.add_data_validations("A1", "A10", "=$A$2:$A$16")
|> Sheet.add_data_validations("B1", "B10", "=$B$2:$B$16")
# reference to other sheet "=#{sheet.name}!$A$2:$A$16"
|> Sheet.add_data_validations("A1", "A10", "=sheet2!$A$2:$A$16")
|> Sheet.add_data_validations("C1", "C10", "=Third!$A$2:$A$16")

workbook = %Workbook{sheets: [sheet1]}
workbook = %Workbook{
sheets: [sheet1],
font: "Arial",
font_size: 12
}

# it is also possible to add a custom "created" date to workbook, otherwise,
# the current date is used.
Expand All @@ -65,7 +70,7 @@ sheet2 =
name: "Third",
rows: [[1, 2, 3, 4, 5], [1, 2], ["increased row height"], ["hello", "world"]]
}
|> Sheet.set_row_height(3, 40)
|> Sheet.set_row_height(2, 75)

workbook = Workbook.append_sheet(workbook, sheet2)

Expand Down Expand Up @@ -136,7 +141,7 @@ sheet5 =
sheet6 =
%Sheet{
name: "Row and Column Groups",
rows: 1..100 |> Enum.chunk(10),
rows: 1..100 |> Enum.chunk_every(10),
# collapse and hide rows 2 to 3
group_rows: [{2..3, collapsed: true}, 6..7],
# nest
Expand All @@ -145,7 +150,70 @@ sheet6 =
# nest further
|> Sheet.group_cols("C", "D")

# Images
sheet7 = %Sheet{
name: "Images",
rows: List.duplicate(["A", "B", "C", "D", "E"], 5)
}

sheet7 =
sheet7
|> Sheet.set_col_width("A", 10)
|> Sheet.set_col_width("B", 10)
|> Sheet.set_col_width("C", 10)
|> Sheet.set_col_width("D", 10)
|> Sheet.set_col_width("E", 10)
|> Sheet.set_row_height(1, 75)
|> Sheet.set_row_height(2, 75)
|> Sheet.set_row_height(3, 75)
|> Sheet.set_row_height(4, 75)
|> Sheet.set_row_height(5, 75)
# Images can be aligned from the right, but you
# might need to adjust the char (max character width)
# which is different per font and size.
|> Sheet.insert_image(1, 1, "ladybug-3475779_640.jpg",
width: 50,
height: 50,
align_x: :right,
char: 9
)
|> Sheet.insert_image(2, 2, "ladybug-3475779_640.jpg",
width: 100,
height: 100
)
# Pass in the binary instead of loading from a file path
|> Sheet.insert_image(3, 0, {"ladybug-3475779_640.jpg", File.read!("ladybug-3475779_640.jpg")},
width: 150,
height: 150
)

sheet8 = %Sheet{
name: "Images 2",
rows: List.duplicate(["A", "B", "C", "D", "E"], 5)
}

sheet8 =
sheet8
|> Sheet.set_col_width("A", 10)
|> Sheet.set_col_width("B", 10)
|> Sheet.set_col_width("C", 10)
|> Sheet.set_col_width("D", 10)
|> Sheet.set_col_width("E", 10)
|> Sheet.set_row_height(1, 75)
|> Sheet.set_row_height(2, 75)
|> Sheet.set_row_height(3, 75)
|> Sheet.set_row_height(4, 75)
|> Sheet.set_row_height(5, 75)
|> Sheet.insert_image(0, 0, "ladybug-3475779_640.jpg",
width: 100,
height: 100,
x_offset: 50,
y_offset: 50
)

Workbook.append_sheet(workbook, sheet4)
|> Workbook.append_sheet(sheet5)
|> Workbook.append_sheet(sheet6)
|> Workbook.append_sheet(sheet7)
|> Workbook.append_sheet(sheet8)
|> Elixlsx.write_to("example.xlsx")
Binary file added ladybug-3475779_640.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion lib/elixlsx/color.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defmodule Elixlsx.Color do
"FF" <>
(color
# remove leading character
|> String.slice(1..-1)
|> String.slice(1..-1//1)
|> String.upcase())

false ->
Expand Down
38 changes: 37 additions & 1 deletion lib/elixlsx/compiler.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
defmodule Elixlsx.Compiler do
alias Elixlsx.Compiler.WorkbookCompInfo
alias Elixlsx.Compiler.SheetCompInfo
alias Elixlsx.Compiler.DrawingCompInfo
alias Elixlsx.Compiler.CellStyleDB
alias Elixlsx.Compiler.DrawingDB
alias Elixlsx.Compiler.StringDB
alias Elixlsx.XML
alias Elixlsx.Sheet
Expand All @@ -25,6 +27,26 @@ defmodule Elixlsx.Compiler do
{Enum.reverse(sheetCompInfos), nextrID}
end

@doc """
Turn a list of %Sheet{} into a list of %DrawingCompInfo{}
and return the next rId after.
"""
@spec make_drawing_info(nonempty_list(Sheet.t()), non_neg_integer) ::
{list(DrawingCompInfo.t()), non_neg_integer}
def make_drawing_info(sheets, init_rId) do
{dcis, _, next_id} =
Enum.reduce(sheets, {[], 1, init_rId}, fn acc, {dci, idx, rId} ->
if acc.images == [] do
{dci, idx, rId}
else
new_dci = DrawingCompInfo.make(idx, rId)
{dci ++ [new_dci], idx + 1, rId + 1}
end
end)

{dcis, next_id}
end

def compinfo_cell_pass_value(wci, value) do
cond do
is_binary(value) && XML.valid?(value) ->
Expand Down Expand Up @@ -59,6 +81,16 @@ defmodule Elixlsx.Compiler do
end
end

def compinfo_image_pass(wci, image) do
update_in(wci.drawingdb, &DrawingDB.register_image(&1, image))
end

def compinfo_from_images(wci, images) do
List.foldl(images, wci, fn image, wci ->
compinfo_image_pass(wci, image)
end)
end

@spec compinfo_from_rows(WorkbookCompInfo.t(), list(list(any()))) :: WorkbookCompInfo.t()
def compinfo_from_rows(wci, rows) do
List.foldl(rows, wci, fn cols, wci ->
Expand All @@ -71,16 +103,20 @@ defmodule Elixlsx.Compiler do
@spec compinfo_from_sheets(WorkbookCompInfo.t(), list(Sheet.t())) :: WorkbookCompInfo.t()
def compinfo_from_sheets(wci, sheets) do
List.foldl(sheets, wci, fn sheet, wci ->
compinfo_from_rows(wci, sheet.rows)
wci
|> compinfo_from_rows(sheet.rows)
|> compinfo_from_images(sheet.images)
end)
end

@first_free_rid 2
def make_workbook_comp_info(workbook) do
{sci, next_rId} = make_sheet_info(workbook.sheets, @first_free_rid)
{dci, next_rId} = make_drawing_info(workbook.sheets, next_rId)

%WorkbookCompInfo{
sheet_info: sci,
drawing_info: dci,
next_free_xl_rid: next_rId
}
|> compinfo_from_sheets(workbook.sheets)
Expand Down
27 changes: 27 additions & 0 deletions lib/elixlsx/compiler/drawing_comp_info.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule Elixlsx.Compiler.DrawingCompInfo do
alias __MODULE__

@moduledoc ~S"""
Compilation info for a Drawing, to be filled
during the actual write process.
"""

defstruct rId: "",
filename: "drawing1.xml",
drawingId: 0

@type t :: %DrawingCompInfo{
rId: String.t(),
filename: String.t(),
drawingId: non_neg_integer
}

@spec make(non_neg_integer, non_neg_integer) :: DrawingCompInfo.t()
def make(drawingidx, rId) do
%DrawingCompInfo{
rId: "rId" <> to_string(rId),
filename: "drawing" <> to_string(drawingidx) <> ".xml",
drawingId: drawingidx
}
end
end
49 changes: 49 additions & 0 deletions lib/elixlsx/compiler/drawing_db.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
defmodule Elixlsx.Compiler.DrawingDB do
alias __MODULE__
alias Elixlsx.Image

@moduledoc ~S"""
Database of drawing elements in the whole document.

Drawing id values must be unique across the document
regardless of what kind of drawing they are.
"""

defstruct images: %{}, element_count: 0

@type t :: %DrawingDB{
images: %{Image.t() => pos_integer},
element_count: non_neg_integer
}

def register_image(drawingdb, image) do
case Map.fetch(drawingdb.images, image) do
:error ->
%DrawingDB{
images: Map.put(drawingdb.images, image, drawingdb.element_count + 1),
element_count: drawingdb.element_count + 1
}

{:ok, _} ->
drawingdb
end
end

def get_id(drawingdb, image) do
case Map.fetch(drawingdb.images, image) do
:error ->
raise %ArgumentError{
message: "Invalid key provided for DrawingDB.get_id: " <> inspect(image)
}

{:ok, id} ->
id
end
end

def image_types(db) do
db.images
|> Enum.map(fn {i, _} -> {i.extension, i.type} end)
|> Enum.uniq()
end
end
6 changes: 5 additions & 1 deletion lib/elixlsx/compiler/workbook_comp_info.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,29 @@ defmodule Elixlsx.Compiler.WorkbookCompInfo do
required to generate the XML file.

It is used as the aggregator when folding over the individual
cells.
cells and images.
"""
defstruct sheet_info: nil,
drawing_info: nil,
stringdb: %Compiler.StringDB{},
fontdb: %Compiler.FontDB{},
filldb: %Compiler.FillDB{},
cellstyledb: %Compiler.CellStyleDB{},
numfmtdb: %Compiler.NumFmtDB{},
borderstyledb: %Compiler.BorderStyleDB{},
drawingdb: %Compiler.DrawingDB{},
next_free_xl_rid: nil

@type t :: %Compiler.WorkbookCompInfo{
sheet_info: [Compiler.SheetCompInfo.t()],
drawing_info: [Compiler.DrawingCompInfo.t()],
stringdb: Compiler.StringDB.t(),
fontdb: Compiler.FontDB.t(),
filldb: Compiler.FillDB.t(),
cellstyledb: Compiler.CellStyleDB.t(),
numfmtdb: Compiler.NumFmtDB.t(),
borderstyledb: Compiler.BorderStyleDB.t(),
drawingdb: Compiler.DrawingDB.t(),
next_free_xl_rid: non_neg_integer
}
end
Loading