Skip to content

Commit

Permalink
Add User Fonts
Browse files Browse the repository at this point in the history
  • Loading branch information
jwharm committed Oct 29, 2023
1 parent 0bf2378 commit 83f1be0
Show file tree
Hide file tree
Showing 16 changed files with 1,308 additions and 14 deletions.
13 changes: 13 additions & 0 deletions src/main/java/io/github/jwharm/cairobindings/Interop.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ public static MemorySegment reinterpret(MemorySegment address, MemoryLayout layo
return MemorySegment.ofAddress(address.address(), layout.byteSize());
}

/**
* Reinterpret a zero-length MemorySegment to the requested size
*
* @param address the address of a memory segment with the requested size
* @param size the requested size
* @return the memory segment with the requested size
*/
public static MemorySegment reinterpret(MemorySegment address, long size) {
if (address.byteSize() == size)
return address;
return MemorySegment.ofAddress(address.address(), size);
}

/**
* Creates a method handle that is used to call the native function with
* the provided name and function descriptor. The method handle is cached
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/freedesktop/cairo/FTFontFace.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
*
* @since 1.0
*/
public class FTFontFace extends FontFace {
public final class FTFontFace extends FontFace {

static {
Cairo.ensureInitialized();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/freedesktop/cairo/FontFace.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
* @see ScaledFont
* @since 1.0
*/
public class FontFace extends Proxy {
public sealed class FontFace extends Proxy permits ToyFontFace, FTFontFace, UserFontFace {

static {
Cairo.ensureInitialized();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/freedesktop/cairo/FontType.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public enum FontType {
*
* @since 1.18
*/
CAIRO_FONT_TYPE_DWRITE;
DWRITE;

static {
Cairo.ensureInitialized();
Expand Down
118 changes: 109 additions & 9 deletions src/main/java/org/freedesktop/cairo/Glyphs.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,26 @@

package org.freedesktop.cairo;

import io.github.jwharm.cairobindings.Interop;
import io.github.jwharm.cairobindings.MemoryCleaner;
import io.github.jwharm.cairobindings.Proxy;

import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;

/**
* The {@code Glyphs} class represents an array of glyphs. It will also optionally
* contain an array of clusters and the accompanying {@link TextClusterFlags}.
*/
public class Glyphs implements AutoCloseable {

private final Proxy glyphsPointer;
private final Proxy clustersPointer;
private final int numGlyphs;
private final int numClusters;
private final TextClusterFlags clusterFlags;
private Proxy glyphsPointer;
private Proxy clustersPointer;
private int numGlyphs;
private int numClusters;
private TextClusterFlags clusterFlags;

/**
* Constructor used internally to instantiate a java Glyphs object for a native
Expand All @@ -54,10 +58,10 @@ public class Glyphs implements AutoCloseable {
this.clustersPointer = new Proxy(clustersPtr);
this.numClusters = numClusters;
this.clusterFlags = clusterFlags;
MemoryCleaner.setFreeFunc(this.glyphsPointer.handle(), "cairo_glyph_free");
MemoryCleaner.setFreeFunc(this.clustersPointer.handle(), "cairo_text_cluster_free");
MemoryCleaner.takeOwnership(this.glyphsPointer.handle());
MemoryCleaner.takeOwnership(this.clustersPointer.handle());
MemoryCleaner.setFreeFunc(glyphsPtr, "cairo_glyph_free");
MemoryCleaner.setFreeFunc(clustersPtr, "cairo_text_cluster_free");
MemoryCleaner.takeOwnership(glyphsPtr);
MemoryCleaner.takeOwnership(clustersPtr);
}

/**
Expand All @@ -68,6 +72,17 @@ public MemorySegment getGlyphsPointer() {
return glyphsPointer.handle();
}

/**
* Set a pointer to the glyphs array
* @param glyphsPointer a MemorySegment pointing to the glyphs array
*/
public void setGlyphsPointer(MemorySegment glyphsPointer) {
MemoryCleaner.free(this.glyphsPointer.handle());
this.glyphsPointer = new Proxy(glyphsPointer);
MemoryCleaner.setFreeFunc(glyphsPointer, "cairo_glyph_free");
MemoryCleaner.takeOwnership(glyphsPointer);
}

/**
* Get the number of glyphs in the array
* @return the number of glyphs
Expand All @@ -76,6 +91,14 @@ public int getNumGlyphs() {
return numGlyphs;
}

/**
* Set the number of glyphs in the array
* @param numGlyphs the number of glyphs
*/
public void setNumGlyphs(int numGlyphs) {
this.numGlyphs = numGlyphs;
}

/**
* Get a pointer to the clusters array
* @return a zero-length MemorySegment pointing to the clusters array
Expand All @@ -84,6 +107,17 @@ public MemorySegment getClustersPointer() {
return clustersPointer.handle();
}

/**
* Set a pointer to the clusters array
* @param clustersPointer the MemorySegment pointing to the clusters array
*/
public void setClustersPointer(MemorySegment clustersPointer) {
MemoryCleaner.free(this.clustersPointer.handle());
this.clustersPointer = new Proxy(clustersPointer);
MemoryCleaner.setFreeFunc(clustersPointer, "cairo_text_cluster_free");
MemoryCleaner.takeOwnership(clustersPointer);
}

/**
* Get the number of text clusters in the cluster array
* @return the number of text clusters
Expand All @@ -92,6 +126,14 @@ public int getNumClusters() {
return numClusters;
}

/**
* Set the number of text clusters in the cluster array
* @param numClusters the number of text clusters
*/
public void setNumClusters(int numClusters) {
this.numClusters = numClusters;
}

/**
* Get the text cluster flags
* @return the text cluster flags
Expand All @@ -100,6 +142,14 @@ public TextClusterFlags getClusterFlags() {
return clusterFlags;
}

/**
* Set the text cluster flags
* @param clusterFlags the text cluster flags
*/
public void setClusterFlags(TextClusterFlags clusterFlags) {
this.clusterFlags = clusterFlags;
}

/**
* Free the glyphs array using {@code cairo_glyph_free} and the clusters array
* using {@code cairo_text_cluster_free}.
Expand All @@ -109,4 +159,54 @@ public void close() {
MemoryCleaner.free(glyphsPointer.handle());
MemoryCleaner.free(clustersPointer.handle());
}

/**
* Allocates an array of {@link Glyph}s. This function is only useful in
* implementations of {@link UserScaledFontTextToGlyphsFunc} where the user
* needs to allocate an array of glyphs that cairo will free. For all other
* uses, user can use their own allocation method for glyphs.
* <p>
* This function returns {@link MemorySegment#NULL} if {@code numGlyphs} is not
* positive, or if out of memory. That means, the {@code NULL} return value
* signals out-of-memory only if {@code numGlyphs} was positive.
*
* @param numGlyphs number of glyphs to allocate
* @return the newly allocated array of glyphs
* @since 1.8
*/
public static MemorySegment allocateGlyphs(int numGlyphs) {
try {
return (MemorySegment) cairo_glyph_allocate.invoke(numGlyphs);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}

private static final MethodHandle cairo_glyph_allocate = Interop.downcallHandle(
"cairo_glyph_allocate", FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_INT));

/**
* Allocates an array of {@link TextCluster}s. This function is only useful in
* implementations of {@link UserScaledFontTextToGlyphsFunc} where the user
* needs to allocate an array of text clusters that cairo will free. For all
* other uses, user can use their own allocation method for text clusters.
* <p>
* This function returns {@link MemorySegment#NULL} if {@code numClusters} is
* not positive, or if out of memory. That means, the {@code NULL} return value
* signals out-of-memory only if {@code numClusters} was positive.
*
* @param numClusters number of TextClusters to allocate
* @return the newly allocated array of text clusters
* @since 1.8
*/
public static MemorySegment allocateClusters(int numClusters) {
try {
return (MemorySegment) cairo_text_cluster_allocate.invoke(numClusters);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}

private static final MethodHandle cairo_text_cluster_allocate = Interop.downcallHandle(
"cairo_text_cluster_allocate", FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_INT));
}
21 changes: 21 additions & 0 deletions src/main/java/org/freedesktop/cairo/Pattern.java
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,25 @@ public static org.gnome.glib.Type getType() {

private static final MethodHandle cairo_gobject_pattern_get_type = Interop.downcallHandle(
"cairo_gobject_pattern_get_type", FunctionDescriptor.of(ValueLayout.JAVA_LONG));

/**
* Package-private helper class used internally to instantiate Pattern objects
* for native {@code cairo_pattern_t} structs of unspecified type, while keeping
* the base Pattern class abstract. This is used for the Pattern objects
* returned by {@link UserScaledFont#getForegroundMarker()} and
* {@link UserScaledFont#getForegroundSource()}.
*/
static class PatternImpl extends Pattern {

/**
* Constructor used internally to instantiate a java Pattern object for a
* native {@code cairo_pattern_t} instance
*
* @param address the memory address of the native {@code cairo_pattern_t}
* instance
*/
public PatternImpl(MemorySegment address) {
super(address);
}
}
}
9 changes: 8 additions & 1 deletion src/main/java/org/freedesktop/cairo/ScaledFont.java
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,14 @@ public Glyphs textToGlyphs(double x, double y, String string) throws IllegalStat
public FontFace getFontFace() {
try {
MemorySegment result = (MemorySegment) cairo_scaled_font_get_font_face.invoke(handle());
FontFace fontFace = new FontFace(result);
FontFace temp = new FontFace(result);
// Instantiate the correct FontFace subclass for the FontType
FontFace fontFace = switch(temp.getFontType()) {
case TOY -> new ToyFontFace(result);
case FT -> new FTFontFace(result);
case USER -> new UserFontFace(result);
default -> temp;
};
// Take a reference on the returned fontface
fontFace.reference();
MemoryCleaner.takeOwnership(fontFace.handle());
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/freedesktop/cairo/ToyFontFace.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
* @see FontFace
* @since 1.8
*/
public class ToyFontFace extends FontFace {
public final class ToyFontFace extends FontFace {

static {
Cairo.ensureInitialized();
Expand Down
Loading

0 comments on commit 83f1be0

Please sign in to comment.