Skip to content

Commit

Permalink
feat: og image plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
mauricerenck committed Oct 30, 2024
1 parent 652ff77 commit de17439
Show file tree
Hide file tree
Showing 13 changed files with 7,609 additions and 83 deletions.
14 changes: 14 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/content/ export-ignore
/site/ export-ignore
/src/ export-ignore
/media/ export-ignore
/tests/ export-ignore
/.github/ export-ignore

/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.htaccess export-ignore
/index.site.php export-ignore
/package.json export-ignore
/package-lock.json export-ignore
28 changes: 28 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI
on:
push:
branches:
- main
jobs:
release:
name: Release
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 21

- name: Install
run: npm install

- name: Release
env:
GITHUB_TOKEN: ${{ secrets.PAT_SEMANTIC_RELEASE }}
run: npx semantic-release
43 changes: 43 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# files of Composer dependencies that are not needed for the plugin
/vendor/**/.*
/vendor/**/*.json
/vendor/**/*.txt
/vendor/**/*.md
/vendor/**/*.yml
/vendor/**/*.yaml
/vendor/**/*.xml
/vendor/**/*.dist
/vendor/**/readme.php
/vendor/**/LICENSE
/vendor/**/COPYING
/vendor/**/VERSION
/vendor/**/docs/*
/vendor/**/example/*
/vendor/**/examples/*
/vendor/**/test/*
/vendor/**/tests/*
/vendor/**/php4/*
/vendor/getkirby/composer-installer
/vendor/doctrine
/vendor/myclabs
/vendor/phar-io
/vendor/phpdocumentor
/vendor/phpspec
/vendor/phpunit
/vendor/sebastian
/vendor/symfony
/vendor/theseer
/vendor/webmozart
/.cache
/node_modules
/site/sessions
/site/accounts
/media
/.vscode
/kirby
/.sqlite
/site/cache
/content/phpunit-test
/~
/content/phpunit/.lock
/.ddev
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v21.11.0
52 changes: 52 additions & 0 deletions .releaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"tagFormat": "v${version}",
"branches": [
"main"
],
"plugins": [
"@semantic-release/commit-analyzer",
{
"preset": "angular",
"releaseRules": [
{
"type": "docs",
"release": "patch"
},
{
"type": "refactor",
"release": "patch"
},
{
"type": "improvement",
"release": "patch"
}
]
},
"@semantic-release/release-notes-generator",
"@ambimax/semantic-release-composer",
"@semantic-release/github",
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
[
"@ambimax/semantic-release-composer",
{
"skipOnMissingComposerJson": true
}
],
[
"@semantic-release/git",
{
"assets": [
"composer.json",
"package.json",
"CHANGELOG.md"
],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# OG Image

#### A Kirby OpenGraph Image Plugin

![GitHub release](https://img.shields.io/github/release/mauricerenck/og-image.svg?maxAge=1800) ![License](https://img.shields.io/github/license/mashape/apistatus.svg) ![Kirby Version](https://img.shields.io/badge/Kirby-4%2B-black.svg)

---

This plugin creates an og-image for a page based on a template image and a text input. Simply add `/og-image` to any url to get the og-image for that page.

## Installation

Use one of these methods to install the plugin:

- composer (recommended): `composer require mauricerenck/komments`
- zip file: unzip [main.zip](https://github.com/mauricerenck/komments/releases/latest) as folder `site/plugins/komments`

## Prerequisites

This plugin requires the following assets to be present:

- a ttf font file
- a png template image

You can find a sample template image in the `assets` folder of this plugin.

## How it works

This plugins listens to `/og-image` on any page. It will go through the following steps:

1. Check if the page has a `ogimage` field - If you have an `ogimage` field, the plugin will use the image from that field and deliver it as the og-image.
2. Check if the page has a `hero` image - If you have a `hero` image, the plugin can use that image and place it under the template image.
3. Use the template image and place text on it.

You can configure the position of the text, and the hero image, even crop it to position it below transparent areas of your template image.

## OPTIONS

**Please make sure to prefix all the options with `mauricerenck.ogimage.`**.

| Option | Default | Description |
| --------------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `width` | `1600` | width of the resulting og-image |
| `height` | `900` | height of the resulting og-image |
| `image.template` | `./../assets/template.png` | path to your og-image template image |
| `font.path` | `''` | **mandatory** (missing font will result in an error) |
| `font.color` | `[0, 0, 0]` | color of the font [r,g,b] |
| `font.size` | `80` | size of the font |
| `heroImage.field` | `hero` | path to your og-image template image |
| `heroImage.cropSize` | `[600, 600]` | Size in pixels of the rendered hero image |
| `heroImage.position` | `[0,0]` | x,y position of the hero image on the template image |
| `heroImage.fallbackColor` | `[255, 123, 123]` | [r,g,b] color to fill the hero-image area if no image given |
| `heroImage.fallbackImage` | `null` | path to a fallback when the hero image is not given |
| `title.field` | `title` | The name of the field your want to use as title |
| `title.position` | `[0, 0]` | [x,y] position of your text |
| `title.charactersPerLine` | `20` | Number of characters before a line break |
Binary file removed assets/background.png
Binary file not shown.
Binary file added assets/template.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 20 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
{
"name": "mauricerenck/ogimage",
"version": "1.0.0",
"homepage": "https://github.com/mauricerenck/og-image",
"description": "Creates an Open Graph Image for each page",
"type": "kirby-plugin",
"license": "MIT",
"authors": [
{
"name": "Maurice Renck",
"email": "[email protected]"
}
]
],
"autoload": {
"psr-4": {
"mauricerenck\\Komments\\": "lib/"
},
"classmap": [
"lib"
]
},
"config": {
"optimize-autoloader": true,
"allow-plugins": {
"getkirby/composer-installer": true
}
},
"require": {
"php": ">=8.2.0"
}
}
111 changes: 29 additions & 82 deletions index.php
Original file line number Diff line number Diff line change
@@ -1,94 +1,41 @@
<?php
kirby::plugin('mauricerenck/ogimage', [

namespace mauricerenck\OgImage;

use Kirby\Http\Response;
use Kirby\Cms\App as Kirby;

Kirby::plugin('mauricerenck/ogimage', [
'pageMethods' => require_once __DIR__ . '/src/page-methods.php',
'routes' => [
[
'pattern' => '(:all)/og-image',
'action' => function ($slug) {
if (strpos($slug, 'de/') === 0 || strpos($slug, 'de/') === 0) {
$pageSlug = substr($slug, 3);
}
$pageSlug = (empty($pageSlug) || $pageSlug === 'de' || $pageSlug === 'en') ? '/home' : $pageSlug;
$page = page($pageSlug);
'language' => '*',
'action' => function ($lang, $slug) {
$page = ($slug == $lang) ? site()->homePage() : $page = page($slug);

$imageWidth = option('mauricerenck.ogimage.width', 1600);
$imageHeight = option('mauricerenck.ogimage.height', 900);

if (!$page) {
return;
return new Response('Page "' . $slug . '" not found', 'text/plain', 404);
}

$seoTitle = ($page->seoTitle()->isNotEmpty()) ? $page->seoTitle() : $page->title() ;

$canvas = imagecreatetruecolor(1600, 900);

// Define colors and fonts
$black = imagecolorallocate($canvas, 0, 0, 0);
$white = imagecolorallocate($canvas, 255, 255, 255);
$purple = imagecolorallocate($canvas, 139, 126, 164);

$fontRegular = __DIR__ . '/assets/GangsterGrotesk-Regular.ttf';
$fontBold = __DIR__ . '/assets/GangsterGrotesk-Bold.ttf';

$background = imagecreatefrompng(__DIR__ . '/assets/background.png');
imagecopyresampled(
$canvas,
$background,
0,
0,
0,
0,
imagesx($background),
imagesy($background),
imagesx($background),
imagesy($background)
);

// Lead text
$text = wordwrap($seoTitle, 15, "\n");

$text_box = imagettfbbox(80, 0, $fontBold, $text);
// Get your Text Width and Height
$text_width = $text_box[2] - $text_box[0];
$text_height = $text_box[1] - $text_box[7];

// Calculate coordinates of the text (centered)
// $x = (1280 / 2) - ($text_width / 2); // centered
$y = (1000 / 2) - ($text_height / 2); // centered
$x = 150;

[$titleX, $titleY] = imagettftext(
$canvas,
80,
0,
$x,
$y,
$white,
$fontBold,
$text
);

// DESCRIPTION
// if ($titleY <= 415) {
// $description = $page->seoDescription()->or($page->intro()->excerpt(200))->excerpt(200);

// $text = wordwrap($description, 48, "\n");
// imagettftext(
// $canvas,
// 24,
// 0,
// 250,
// $titleY + 150,
// $white,
// $fontRegular,
// $text
// );
// }
if ($page->ogimage()->isNotEmpty()) {
return new Response($page->ogimage()->toFile()->crop($imageWidth, $imageHeight)->read(), 'image/png');
}

// Render
ob_start();
imagepng($canvas);
$body = ob_get_clean();
imagedestroy($canvas);
if ($page->hasGeneratedOgImage()) {
return new Response($page->image('generated-og-image.' . $lang . '.png')->read(), 'image/png');
}

return new Response($body, 'image/png');
}
try {
$page->createOgImage();
return new Response($page->image('generated-og-image.' . $lang . '.png')->read(), 'image/png');
} catch (\Exception $e) {
return $e->getMessage();
}
},
],
]
],
]);
Loading

0 comments on commit de17439

Please sign in to comment.