⚠️ BETA SOFTWARE NOTICE This package is currently in beta and is being prepared for testing in upcoming projects. Please expect possible breaking changes in future releases. We do not recommend using this package in production environments without thorough testing.
A Laravel package that provides a PHP wrapper around AsciiDoctor and AsciiDoctor-PDF with advanced theming, styling, and configuration management.
This package bridges the gap between PHP applications and the powerful AsciiDoctor ecosystem, providing:
- Configurable AsciiDoctor Integration: Support for both
asciidoctorandasciidoctor-pdfexecutables - Advanced Theming System: Create, extend, and manage themes with JSON configuration
- Style Object Composition: Programmatically build themes using fluent APIs
- Theme Inheritance: Extend built-in AsciiDoctor themes or custom themes
- Laravel Integration: Service providers, facades, and configuration management
- Type Safety: Full PHP 8.2+ type declarations and comprehensive testing
Install the package via Composer:
composer require ubertech-za/asciidoctor-wrapperYou need to have AsciiDoctor installed on your system:
# Install AsciiDoctor (Ruby required)
gem install asciidoctor
# Install AsciiDoctor-PDF for PDF generation
gem install asciidoctor-pdfAlternatively, use Docker:
docker pull asciidoctor/docker-asciidoctorPublish the configuration file:
php artisan vendor:publish --tag=asciidoctor-configConfigure your executables in config/asciidoctor.php:
return [
'executables' => [
'asciidoctor' => env('ASCIIDOCTOR_PATH', 'asciidoctor'),
'asciidoctor_pdf' => env('ASCIIDOCTOR_PDF_PATH', 'asciidoctor-pdf'),
],
'default_theme' => env('ASCIIDOCTOR_DEFAULT_THEME', 'default'),
'themes_path' => env('ASCIIDOCTOR_THEMES_PATH', storage_path('asciidoc/themes')),
'templates_path' => env('ASCIIDOCTOR_TEMPLATES_PATH', storage_path('asciidoc/templates')),
'fonts_path' => env('ASCIIDOCTOR_FONTS_PATH', storage_path('asciidoc/fonts')),
'output_path' => env('ASCIIDOCTOR_OUTPUT_PATH', storage_path('asciidoc/documents')),
// ... additional configuration
];Add to your .env file:
ASCIIDOCTOR_PATH=/usr/local/bin/asciidoctor
ASCIIDOCTOR_PDF_PATH=/usr/local/bin/asciidoctor-pdf
ASCIIDOCTOR_DEFAULT_THEME=default-with-font-fallbacks
ASCIIDOCTOR_THEMES_PATH=/path/to/themes
ASCIIDOCTOR_TEMPLATES_PATH=/path/to/templates
ASCIIDOCTOR_FONTS_PATH=/path/to/your/fonts
ASCIIDOCTOR_OUTPUT_PATH=/path/to/output
ASCIIDOCTOR_CACHE_ENABLED=true
ASCIIDOCTOR_CACHE_TTL=3600use UbertechZa\AsciidoctorWrapper\Facades\Asciidoctor;
// Convert AsciiDoc to HTML
$result = Asciidoctor::convert(
input: 'document.adoc',
output: 'document.html',
format: 'html5'
);
// Convert to PDF
$result = Asciidoctor::convert(
input: 'document.adoc',
output: 'document.pdf',
format: 'pdf'
);
if ($result['success']) {
echo "Conversion successful!";
} else {
echo "Error: " . $result['error'];
}use UbertechZa\AsciidoctorWrapper\AsciidoctorWrapper;
$wrapper = new AsciidoctorWrapper(
asciidoctorPath: '/usr/local/bin/asciidoctor',
asciidoctorPdfPath: '/usr/local/bin/asciidoctor-pdf'
);
$result = $wrapper->convert('input.adoc', 'output.pdf', 'pdf');use UbertechZa\AsciidoctorWrapper\Builder\ThemeBuilder;
$theme = app(ThemeBuilder::class)
->name('modern-theme')
->extending('default')
->withColors([
'primary' => '#2563eb',
'secondary' => '#64748b',
'accent' => '#059669',
'text' => '#0f172a',
'background' => '#ffffff'
])
->withFont('heading', 'Inter', '16pt', 'bold')
->withFont('body', 'Inter', '11pt', 'normal')
->withFont('mono', 'JetBrains Mono', '9pt', 'normal')
->withElementStyle('document', [
'color' => '#0f172a',
'font' => ['family' => 'Inter', 'size' => '11pt']
])
->withElementStyle('heading1', [
'color' => '#2563eb',
'font' => ['family' => 'Inter', 'size' => '24pt', 'weight' => 'bold']
])
->build();
// Use the theme
$result = Asciidoctor::convert('input.adoc', 'output.pdf', 'pdf', $theme);Create a theme file at resources/asciidoctor/themes/my-theme.json:
{
"name": "my-theme",
"extends": "default",
"colors": {
"primary": "#2563eb",
"secondary": "#64748b",
"accent": "#059669",
"text": "#0f172a",
"background": "#ffffff"
},
"fonts": {
"heading": {
"family": "Inter",
"size": "16pt",
"weight": "bold"
},
"body": {
"family": "Inter",
"size": "11pt",
"weight": "normal"
},
"mono": {
"family": "JetBrains Mono",
"size": "9pt",
"weight": "normal"
}
},
"styles": {
"document": {
"color": "#0f172a",
"font": {
"family": "Inter",
"size": "11pt"
}
},
"heading1": {
"color": "#2563eb",
"font": {
"family": "Inter",
"size": "24pt",
"weight": "bold"
}
}
}
}Load and use the theme:
use UbertechZa\AsciidoctorWrapper\StyleManager;
$styleManager = app(StyleManager::class);
$theme = $styleManager->loadFromJson(resource_path('asciidoctor/themes/my-theme.json'));
$result = Asciidoctor::convert('input.adoc', 'output.pdf', 'pdf', $theme);Themes can extend other themes:
{
"name": "dark-theme",
"extends": "modern-theme",
"colors": {
"text": "#ffffff",
"background": "#1a1a1a"
}
}Built-in themes can also be extended:
{
"name": "custom-default",
"extends": "default",
"colors": {
"primary": "#ff6b35"
}
}The package supports custom fonts for PDF generation. Fonts are automatically loaded from the configured fonts directory and made available to asciidoctor-pdf themes.
- Configure the fonts directory in
config/asciidoctor.php:
'fonts_path' => env('ASCIIDOCTOR_FONTS_PATH', storage_path('asciidoc/fonts')),- Add font files to your fonts directory:
storage/asciidoc/fonts/
├── MyFont-Regular.ttf
├── MyFont-Bold.ttf
├── MyFont-Italic.ttf
├── MyFont-BoldItalic.ttf
├── MonoFont-Regular.ttf
└── MonoFont-Bold.ttf
- Use fonts in themes - the package automatically generates the font catalog:
$theme = app(ThemeBuilder::class)
->name('custom-font-theme')
->extending('default-with-font-fallbacks')
->withColors([
'primary' => '#2563eb',
'text' => '#0f172a'
])
->build();When using custom fonts, the package automatically:
- Scans the
fonts_pathdirectory for TTF files - Generates a proper font catalog in the theme YAML
- Sets the
pdf-fontsdirattribute for asciidoctor-pdf - Maps fonts to appropriate theme elements (base, heading, code)
Example generated theme YAML:
extends: default-with-font-fallbacks
font:
catalog:
merge: true
MyFont:
normal: MyFont-Regular.ttf
bold: MyFont-Bold.ttf
italic: MyFont-Italic.ttf
bold_italic: MyFont-BoldItalic.ttf
MonoFont:
normal: MonoFont-Regular.ttf
bold: MonoFont-Bold.ttf
base:
font_family: MyFont
font_size: 11
font_color: 0f172a
heading:
font_family: MyFont
font_color: 2563eb
font_style: bold
code:
font_family: MonoFont
font_size: 9Set the fonts directory via environment variable:
# Use absolute path
ASCIIDOCTOR_FONTS_PATH=/usr/share/fonts/custom
# Or relative to Laravel storage
ASCIIDOCTOR_FONTS_PATH=/path/to/laravel/storage/asciidoc/fonts- TTF (TrueType) - Primary supported format
- OTF (OpenType) - Also supported by asciidoctor-pdf
For automatic detection of font variants, use this naming pattern:
FontName-Regular.ttf(normal weight)FontName-Bold.ttf(bold weight)FontName-Italic.ttf(italic style)FontName-BoldItalic.ttf(bold italic)
html/html5- HTML outputdocbook/docbook5- DocBook XMLmanpage- Unix manual pagespdf- PDF documents (requires asciidoctor-pdf)docx- Microsoft Word documents
// Generate multiple formats
$formats = ['html5', 'pdf', 'docx'];
foreach ($formats as $format) {
$result = Asciidoctor::convert(
input: 'document.adoc',
output: "document.{$format}",
format: $format,
theme: $theme
);
}$result = Asciidoctor::convert(
input: 'document.adoc',
output: 'document.html',
format: 'html5',
attributes: [
'source-highlighter' => 'rouge',
'icons' => 'font',
'sectanchors' => true,
'toc' => 'left',
'toclevels' => 3
]
);$result = Asciidoctor::convert(
input: 'document.adoc',
output: 'document.html',
format: 'html5',
safeMode: 'safe' // unsafe, safe, server, secure
);use UbertechZa\AsciidoctorWrapper\Style\ColorStyle;
$colors = new ColorStyle(
primary: '#2563eb',
secondary: '#64748b',
accent: '#059669',
text: '#0f172a',
background: '#ffffff'
);
// Validate colors
if ($colors->isValidHex('#2563eb')) {
echo "Valid hex color!";
}use UbertechZa\AsciidoctorWrapper\Style\FontStyle;
$font = new FontStyle(
family: 'Inter',
size: '12pt',
weight: 'bold',
style: 'normal'
);
// Convert to array for processing
$fontArray = $font->toArray();use UbertechZa\AsciidoctorWrapper\Style\ElementStyle;
$heading = new ElementStyle(
color: '#2563eb',
backgroundColor: '#ffffff',
font: new FontStyle('Inter', '18pt', 'bold'),
margin: '1em 0',
padding: '0.5em'
);$wrapper = app(AsciidoctorWrapper::class);
if ($wrapper->isAsciidoctorAvailable()) {
echo "AsciiDoctor is available!";
}
if ($wrapper->isAsciidoctorPdfAvailable()) {
echo "AsciiDoctor-PDF is available!";
}
$formats = $wrapper->getSupportedFormats();
// ['html', 'html5', 'docbook', 'docbook5', 'manpage', 'pdf', 'docx']use UbertechZa\AsciidoctorWrapper\StyleManager;
$styleManager = app(StyleManager::class);
$theme = $styleManager->loadFromJson('theme.json');
if ($styleManager->validateTheme($theme)) {
echo "Theme is valid!";
} else {
$errors = $styleManager->getValidationErrors();
foreach ($errors as $error) {
echo "Error: {$error}\n";
}
}The package doesn't include Artisan commands by default, but you can create your own:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use UbertechZa\AsciidoctorWrapper\Facades\Asciidoctor;
use UbertechZa\AsciidoctorWrapper\Builder\ThemeBuilder;
class ConvertToAsciidoc extends Command
{
protected $signature = 'asciidoc:convert {input} {output} {--format=html5} {--theme=}';
protected $description = 'Convert AsciiDoc files to various formats';
public function handle()
{
$input = $this->argument('input');
$output = $this->argument('output');
$format = $this->option('format');
$themeName = $this->option('theme');
$theme = null;
if ($themeName) {
$theme = app(ThemeBuilder::class)->name($themeName)->build();
}
$result = Asciidoctor::convert($input, $output, $format, $theme);
if ($result['success']) {
$this->info("✅ Successfully converted {$input} to {$output}");
} else {
$this->error("❌ Conversion failed: " . $result['error']);
}
}
}The wrapper returns structured results:
$result = Asciidoctor::convert('input.adoc', 'output.pdf', 'pdf');
if ($result['success']) {
echo "Success! Output: " . $result['output'];
} else {
echo "Error: " . $result['error'];
echo "Exit Code: " . $result['exit_code'];
if (!empty($result['stderr'])) {
echo "STDERR: " . $result['stderr'];
}
}Enable theme caching in your configuration:
'cache' => [
'enabled' => env('ASCIIDOCTOR_CACHE_ENABLED', true),
'path' => storage_path('app/asciidoctor/cache'),
'ttl' => env('ASCIIDOCTOR_CACHE_TTL', 3600),
],- Reuse Theme Objects: Create themes once and reuse them for multiple conversions
- Validate Early: Use
validateTheme()during development to catch issues - Cache Compiled Themes: Enable caching for production environments
- Monitor Resources: AsciiDoctor can be memory-intensive for large documents
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Run tests:
composer test - Commit changes:
git commit -am 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Create a Pull Request
git clone https://github.com/ubertech-za/asciidoctor-wrapper.git
cd asciidoctor-wrapper
composer install
composer testRun the test suite:
composer test
# Run with coverage
composer test-coverage
# Run specific test
vendor/bin/pest tests/Unit/StyleManagerTest.php
# Run integration tests
vendor/bin/pest tests/Feature/This package is open-sourced software licensed under the MIT license.
Please see CHANGELOG for more information on what has changed recently.
- UberTech ZA Team
- All Contributors
This package is inspired by the excellent work of:
- AsciiDoctor Project - The core document processing engine
- AsciiDoctor-PDF - PDF generation capabilities
- Issues: GitHub Issues
- Documentation: Package Documentation
- Discussions: GitHub Discussions
Made by Uber Technologies cc