diff --git a/README.md b/README.md
index 4194abf..f4e2ac6 100644
--- a/README.md
+++ b/README.md
@@ -78,7 +78,14 @@ You can set a page emoji icon by specifying the icon in the headers.
```
-You can set the alignment for all images in the page. Common values are `left`, `center`, and `right`. This adds the `ac:align` attribute to image tags. Can also be set globally via the `--image-align` CLI option (per-page header takes precedence).
+You can set the alignment for all images in the page. Common values are `left`, `center`, and `right`. This adds the `ac:align` attribute to image tags and also sets the corresponding `ac:layout` attribute:
+- `left` → `ac:align="left" ac:layout="align-start"`
+- `center` → `ac:align="center" ac:layout="center"`
+- `right` → `ac:align="right" ac:layout="align-end"`
+
+**Note**: Images with width >= 760px automatically use `ac:align="wide"` with `ac:layout="center"` instead of the configured alignment, as Confluence requires this for wide images.
+
+Custom values are passed through as-is with only the `ac:align` attribute. Can also be set globally via the `--image-align` CLI option (per-page header takes precedence).
Mark supports Go templates, which can be included into article by using path
to the template relative to current working dir, e.g.:
diff --git a/attachment/attachment.go b/attachment/attachment.go
index cd3bd6b..06ce50c 100644
--- a/attachment/attachment.go
+++ b/attachment/attachment.go
@@ -4,11 +4,16 @@ import (
"bytes"
"crypto/sha256"
"encoding/hex"
+ "image"
+ _ "image/gif"
+ _ "image/jpeg"
+ _ "image/png"
"io"
"net/url"
"path"
"path/filepath"
"sort"
+ "strconv"
"strings"
"github.com/kovetskiy/mark/confluence"
@@ -210,12 +215,20 @@ func prepareAttachment(opener vfs.Opener, base, name string) (Attachment, error)
return Attachment{}, karma.Format(err, "unable to read file: %q", attachmentPath)
}
- return Attachment{
+ attachment := Attachment{
Name: name,
Filename: strings.ReplaceAll(name, "/", "_"),
FileBytes: fileBytes,
Replace: name,
- }, nil
+ }
+
+ // Try to detect image dimensions
+ if config, _, err := image.DecodeConfig(bytes.NewReader(fileBytes)); err == nil {
+ attachment.Width = strconv.Itoa(config.Width)
+ attachment.Height = strconv.Itoa(config.Height)
+ }
+
+ return attachment, nil
}
func CompileAttachmentLinks(markdown []byte, attachments []Attachment) []byte {
diff --git a/renderer/fencedcodeblock.go b/renderer/fencedcodeblock.go
index 1e5e9b4..064b099 100644
--- a/renderer/fencedcodeblock.go
+++ b/renderer/fencedcodeblock.go
@@ -139,10 +139,14 @@ func (r *ConfluenceFencedCodeBlockRenderer) renderFencedCodeBlock(writer util.Bu
return ast.WalkStop, err
}
r.Attachments.Attach(attachment)
+
+ effectiveAlign := calculateAlign(r.MarkConfig.ImageAlign, attachment.Width)
+
err = r.Stdlib.Templates.ExecuteTemplate(
writer,
"ac:image",
struct {
+ Align string
Width string
Height string
Title string
@@ -150,6 +154,7 @@ func (r *ConfluenceFencedCodeBlockRenderer) renderFencedCodeBlock(writer util.Bu
Attachment string
Url string
}{
+ effectiveAlign,
attachment.Width,
attachment.Height,
attachment.Name,
@@ -170,10 +175,14 @@ func (r *ConfluenceFencedCodeBlockRenderer) renderFencedCodeBlock(writer util.Bu
return ast.WalkStop, err
}
r.Attachments.Attach(attachment)
+
+ effectiveAlign := calculateAlign(r.MarkConfig.ImageAlign, attachment.Width)
+
err = r.Stdlib.Templates.ExecuteTemplate(
writer,
"ac:image",
struct {
+ Align string
Width string
Height string
Title string
@@ -181,6 +190,7 @@ func (r *ConfluenceFencedCodeBlockRenderer) renderFencedCodeBlock(writer util.Bu
Attachment string
Url string
}{
+ effectiveAlign,
attachment.Width,
attachment.Height,
attachment.Name,
diff --git a/renderer/image.go b/renderer/image.go
index 474ca7f..52d08a2 100644
--- a/renderer/image.go
+++ b/renderer/image.go
@@ -3,6 +3,7 @@ package renderer
import (
"bytes"
"path/filepath"
+ "strconv"
"strings"
"github.com/kovetskiy/mark/attachment"
@@ -15,6 +16,30 @@ import (
"github.com/yuin/goldmark/util"
)
+// calculateAlign determines the appropriate ac:align value based on width
+// Images >= 760px wide use "wide", otherwise use the configured alignment
+func calculateAlign(configuredAlign string, width string) string {
+ if configuredAlign == "" {
+ return ""
+ }
+
+ if width == "" {
+ return configuredAlign
+ }
+
+ // Parse width and check if >= 760
+ widthInt, err := strconv.Atoi(width)
+ if err != nil {
+ return configuredAlign
+ }
+
+ if widthInt >= 760 {
+ return "wide"
+ }
+
+ return configuredAlign
+}
+
type ConfluenceImageRenderer struct {
html.Config
Stdlib *stdlib.Lib
@@ -78,6 +103,8 @@ func (r *ConfluenceImageRenderer) renderImage(writer util.BufWriter, source []by
r.Attachments.Attach(attachments[0])
+ effectiveAlign := calculateAlign(r.ImageAlign, attachments[0].Width)
+
err = r.Stdlib.Templates.ExecuteTemplate(
writer,
"ac:image",
@@ -90,9 +117,9 @@ func (r *ConfluenceImageRenderer) renderImage(writer util.BufWriter, source []by
Attachment string
Url string
}{
- r.ImageAlign,
- "",
- "",
+ effectiveAlign,
+ attachments[0].Width,
+ attachments[0].Height,
string(n.Title),
string(nodeToHTMLText(n, source)),
attachments[0].Filename,
diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go
index 370fcb0..a22665b 100644
--- a/stdlib/stdlib.go
+++ b/stdlib/stdlib.go
@@ -212,6 +212,7 @@ func templates(api *confluence.API) (*template.Template, error) {
`ac:image`: text(
`
Use
Use