Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@
^vignettes/articles$
^doc$
^Meta$
^tools$
^\.claude$
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Type: Package
Package: pikchr
Title: R Wrapper for 'pikchr' (PIC) Diagram Language
Version: 1.1.0
Date: 2026-04-07
Version: 1.1.1
Date: 2026-06-14
Authors@R: c(
person("Andre", "Leite", email = "leite@castlab.org", role = c("aut", "cre")),
person("Hugo", "Vaconcelos", email = "hugo.vasconcelos@ufpe.br", role = "aut"),
Expand Down
13 changes: 13 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# pikchr 1.1.1

## Bug fixes

- Fixed a memory leak in the C interface (`pikchr_c`): the SVG buffer returned by `pikchr()` was never freed, leaking on every diagram render.
- Guarded the C interface against a possible `NULL` return from `pikchr()` that could crash the R session.
- Fixed the `height` argument default in `pikchr()`, which never fell back to the SVG `viewBox` height and produced an empty `height:` CSS rule.
- Aligned the documented argument defaults of `pikchr()` (`width`, `height`, `fontSize`, `fontFamily`, `align`) with the actual function defaults.

## Other

- Regenerated the package logo with a geometrically regular hexagon (the previous SVG was horizontally squished) and fixed an invalid `<def>` element (now `<defs>`) in the logo source.

# pikchr 1.1.0

## New features
Expand Down
12 changes: 6 additions & 6 deletions R/pikchrWrappers.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
#' Pikchr is a diagram language inspired by PIC, and this function allows you to easily embed Pikchr diagrams as SVG images in your HTML or Markdown documents.
#'
#' @param code A character string containing the diagram code written in the Pikchr language.
#' @param width A character string representing the width of the rendered SVG. It accepts standard HTML values such as percentages ("75\%"), pixels ("300px"), or "auto". Defaults to "75\%".
#' @param height A character string representing the height of the rendered SVG. It accepts standard HTML values such as pixels ("300px") or "auto". Defaults to "auto".
#' @param fontSize A character string specifying the font size of the text within the SVG. It accepts standard HTML values such as percentages ("80\%"), pixels, or keywords ("large"). Defaults to "80\%".
#' @param fontFamily A character string specifying the font family for the text in the diagram. Common values include "inherit" (to use the page's default font), "Arial", "Times New Roman", etc. Defaults to "inherit".
#' @param width A character string representing the width of the rendered SVG. It accepts standard HTML values such as percentages ("75\%"), pixels ("300px"), or "auto". If NULL (default), the intrinsic width from the SVG viewBox is used.
#' @param height A character string representing the height of the rendered SVG. It accepts standard HTML values such as pixels ("300px") or "auto". If NULL (default), the intrinsic height from the SVG viewBox is used.
#' @param fontSize A character string specifying the font size of the text within the SVG. It accepts standard HTML values such as percentages ("80\%"), pixels, or keywords ("large"). Defaults to "100\%".
#' @param fontFamily A character string specifying the font family for the text in the diagram. Common values include "inherit" (to use the page's default font), "Arial", "Times New Roman", etc. Defaults to "Jost".
#' @param class A character string specifying a CSS class to apply to the SVG. This can be used to style the SVG with external CSS rules. Defaults to "pikchr".
#' @param align A character string specifying the alignment of the SVG within its container. Options are "center" (default), "left", or "right". If set to "none", no alignment is applied.
#' @param align A character string specifying the alignment of the SVG within its container. Options are "center", "left", or "right". Defaults to "none", in which case no alignment wrapper is applied.
#' @param css A character string containing extra CSS styles to apply directly to the SVG element. This allows additional customization beyond width, height, and font settings. If NULL, no extra styles are applied.
#' @param margin A character string specifying the margin around the SVG element, formatted like a CSS margin rule (e.g., "10px 0 10px 0"). Defaults to "10px 0 10px 0".
#' @param svgOnly Logical, whether to return only the raw SVG code (TRUE) or open the diagram in the Viewer window (FALSE, default).
Expand Down Expand Up @@ -61,7 +61,7 @@ pikchr <- function(code,
bbox_width <- bbox[3] - bbox[1]

if (is.null(width)) width <- bbox_width
if (is.null(width)) width <- bbox_height
if (is.null(height)) height <- bbox_height

styles = paste0("style='width:", width,
";height:", height,
Expand Down
9 changes: 9 additions & 0 deletions cran-comments.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## Resubmission

This is a patch release (1.1.1) fixing bugs found during an internal audit:

- Fixed a memory leak in the C interface (the SVG buffer was never freed).
- Guarded the C interface against a possible NULL return that could crash R.
- Fixed the `height` argument default in `pikchr()`.
- Aligned documented argument defaults with the actual defaults.

## R CMD check results

0 errors | 0 warnings | 0 notes
Expand Down
Binary file modified man/figures/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 29 additions & 30 deletions man/figures/pikchrHex.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions man/pikchr.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 10 additions & 7 deletions src/pikchr.c
Original file line number Diff line number Diff line change
Expand Up @@ -8281,13 +8281,16 @@ return exitCode ? EXIT_FAILURE : EXIT_SUCCESS;


SEXP pikchr_c (SEXP code, SEXP class) {
const char *pcode = STRING_VALUE(code);
const char *pclass = STRING_VALUE(class);
char *zOut;
zOut = pikchr(pcode, pclass, 0, NULL, NULL);
SEXP result;
PROTECT(result = NEW_CHARACTER(1));
SET_STRING_ELT(result, 0, mkChar(zOut));
const char *pcode = CHAR(STRING_ELT(code, 0));
const char *pclass = CHAR(STRING_ELT(class, 0));
char *zOut = pikchr(pcode, pclass, 0, NULL, NULL);
SEXP result = PROTECT(allocVector(STRSXP, 1));
if( zOut ){
SET_STRING_ELT(result, 0, mkChar(zOut));
free(zOut); /* pikchr() returns malloc'd memory */
} else {
SET_STRING_ELT(result, 0, NA_STRING);
}
UNPROTECT(1);
return(result);
}
Expand Down
125 changes: 125 additions & 0 deletions tools/generate_logo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""Generate a geometrically regular hexagon logo for the pikchr R package.

The previous man/figures/pikchrHex.svg had a horizontally squished hexagon
(width/height ratio ~0.73 instead of the regular 0.866) and used an invalid
<def> element instead of <defs>. This script rebuilds the same visual design
(filled hexagon + two concentric hexagonal rings of circles connected by
trimmed edges + two lens arcs + centered "Pikchr" text) using exact regular
pointy-top hexagon geometry.
"""
import math

S3 = math.sqrt(3) / 2.0 # cos(30) -> regular hexagon horizontal factor
R_OUT = 39.34155 # outer circumradius (keeps original height)
R1 = 31.4736 # outer ring (light green) circumradius
R2 = 23.605 # inner ring (medium green) circumradius
CR = 1.44 # small circle radius / edge trim length
PAD = 1.44 # padding around the figure

# Center: top vertex sits PAD below the top edge; left vertex PAD from left edge.
CX = R_OUT * S3 + PAD
CY = R_OUT + PAD
VB_W = 2 * R_OUT * S3 + 2 * PAD
VB_H = 2 * R_OUT + 2 * PAD


def hexagon(R):
"""Pointy-top regular hexagon vertices (y grows downward), clockwise."""
return [
(CX, CY - R), # top
(CX + R * S3, CY - R / 2), # upper-right
(CX + R * S3, CY + R / 2), # lower-right
(CX, CY + R), # bottom
(CX - R * S3, CY + R / 2), # lower-left
(CX - R * S3, CY - R / 2), # upper-left
]


def trim(p, q, d):
"""Point d units from p toward q."""
vx, vy = q[0] - p[0], q[1] - p[1]
n = math.hypot(vx, vy)
return (p[0] + vx / n * d, p[1] + vy / n * d)


def f(v):
return f"{v:.4f}".rstrip("0").rstrip(".")


COL_FILL = "rgb(39,174,96)"
COL_FILL_STROKE = "rgb(25,111,61)"
COL_R1 = "rgb(125,206,160)"
COL_R2 = "rgb(82,190,128)"

parts = []
parts.append(
"<svg style='width:100%;height:auto;font-size:80%;font-family:MonteCarlo;"
"margin:10px 0 10px 0;' xmlns='http://www.w3.org/2000/svg' class=\"pikchr\" "
f"viewBox=\"0 0 {f(VB_W)} {f(VB_H)}\">"
"<defs><style type=\"text/css\">"
"@import url(https://fonts.googleapis.com/css2?family=MonteCarlo:wght@400);"
"</style></defs>"
)

# Filled outer hexagon
hx = hexagon(R_OUT)
d = "M" + "L".join(f"{f(x)},{f(y)}" for x, y in hx) + "Z"
parts.append(
f'<path d="{d}" style="fill:{COL_FILL};stroke-width:{f(CR)};'
f'stroke-linejoin:round;stroke:{COL_FILL_STROKE};" />'
)

# Centered text
parts.append(
f'<text lengthAdjust="spacingAndGlyphs" x="{f(CX)}" y="{f(CY)}" '
'text-anchor="middle" fill="rgb(255,255,255)" font-size="80%" '
'dominant-baseline="central">Pikchr</text>'
)


def ring(R, color):
verts = hexagon(R)
out = []
for vx, vy in verts:
out.append(
f'<circle cx="{f(vx)}" cy="{f(vy)}" r="{f(CR)}" '
f'style="fill:none;stroke-width:0.9648;stroke:{color};" />'
)
for i in range(6):
a, b = verts[i], verts[(i + 1) % 6]
s = trim(a, b, CR)
e = trim(b, a, CR)
out.append(
f'<path d="M{f(s[0])},{f(s[1])}L{f(e[0])},{f(e[1])}" '
f'style="fill:none;stroke-width:0.9648;stroke:{color};" />'
)
return out, verts


r1_parts, _ = ring(R1, COL_R1)
parts.extend(r1_parts)
r2_parts, r2v = ring(R2, COL_R2)
parts.extend(r2_parts)

# Two lens arcs on the inner ring (bulging beyond top/bottom vertices)
BULGE = 2.31
c_bottom = (CX, CY + R2 + BULGE)
c_top = (CX, CY - R2 - BULGE)
lr, ll = r2v[2], r2v[4] # lower-right, lower-left
ur, ul = r2v[1], r2v[5] # upper-right, upper-left
for (p, q, c) in ((lr, ll, c_bottom), (ur, ul, c_top)):
s = trim(p, c, CR)
e = trim(q, c, CR)
parts.append(
f'<path d="M{f(s[0])},{f(s[1])}Q{f(c[0])},{f(c[1])} {f(e[0])},{f(e[1])}" '
f'style="fill:none;stroke-width:0.9648;stroke:{COL_R2};" />'
)

parts.append("</svg>")

with open("man/figures/pikchrHex.svg", "w") as fh:
fh.write("\n".join(parts) + "\n")

print(f"viewBox 0 0 {f(VB_W)} {f(VB_H)} ratio={VB_W/VB_H:.4f}")
print(f"hex width/height ratio = {(2*R_OUT*S3)/(2*R_OUT):.4f} (regular = 0.8660)")
Loading