An open API service indexing awesome lists of open source software.

https://github.com/mateffy/pretext-php

A PHP port of the chenglou/pretext text layout and measurement library. Provides fast, accurate text layout calculation using ICU (Intl) for segmentation and an injectable font measurement interface.
https://github.com/mateffy/pretext-php

Last synced: 1 day ago
JSON representation

A PHP port of the chenglou/pretext text layout and measurement library. Provides fast, accurate text layout calculation using ICU (Intl) for segmentation and an injectable font measurement interface.

Awesome Lists containing this project

README

          

# Pretext PHP 8

A PHP 8 port of the **pretext** text layout and measurement library. Provides fast, accurate text layout calculation using ICU (Intl) for segmentation and an injectable font measurement interface.

## Features

- **Fast text layout** - O(n) complexity for measuring and laying out text
- **Internationalization** - Full Unicode support via ICU (Intl extension)
- **Word segmentation** - Proper handling of CJK, Thai, Arabic, Devanagari, and Myanmar
- **Bidirectional text** - Bidi algorithm support for mixed LTR/RTL text
- **Injectable font measurement** - Use stub measurer for testing or GD/FreeType for production
- **Comprehensive test suite** - Full feature parity with the original TypeScript implementation

> [!IMPORTANT]
> This is a **PHP port** of the brilliant [chenglou/pretext](https://github.com/chenglou/pretext) TypeScript library by **Cheng Lou**.
>
> **All credit for the algorithms, design, and implementation goes to Cheng Lou and the original authors.** This port was created using **Kimi K2.5** and maintains feature parity with the original, adapting it for PHP 8's type system and ecosystem.

## Installation

```bash
composer require pretext/pretext
```

## Requirements

- PHP 8.0+
- `ext-intl` (ICU for word/grapheme segmentation)
- `ext-mbstring` (multibyte string functions)
- `ext-gd` (optional, for production font measurement with FreeType)

## Quick Start

```php
use Pretext\PretextFactory;

// Prepare text for layout
$prepared = PretextFactory::prepare('Hello world', '16px Arial');

// Layout with specific max width and line height
$result = PretextFactory::layout($prepared, 200, 24);

echo "Lines: {$result->lineCount}\n";
echo "Height: {$result->height}px\n";
```

## Usage

### Basic Layout

```php
use Pretext\Pretext;
use Pretext\Measurement\GdFontMeasurer;

// Production: Use GD/FreeType font measurement
$measurer = new GdFontMeasurer('/path/to/fonts', [
'Arial' => 'arial.ttf',
'Inter' => 'Inter-Regular.ttf',
]);
$pretext = new Pretext($measurer);

// Or use the static factory
$prepared = PretextFactory::prepare('Hello world', '16px Arial');
$result = PretextFactory::layout($prepared, 200, 24);
```

### Rich Layout (with line details)

```php
$prepared = $pretext->prepareWithSegments('Hello world this is a test', '16px Arial');
$lines = $pretext->layoutWithLines($prepared, 100, 24);

foreach ($lines->lines as $line) {
echo "Line: {$line->text}\n";
echo "Width: {$line->width}px\n";
echo "Start: segment {$line->start->segmentIndex}\n";
}
```

### Streaming Layout

```php
$prepared = $pretext->prepareWithSegments('Long text here...', '16px Arial');
$cursor = new LayoutCursor(0, 0);

while (true) {
$line = $pretext->layoutNextLine($prepared, $cursor, 200);
if ($line === null) break;

echo "Line: {$line->text}\n";
$cursor = $line->end;
}
```

### Custom Font Measurement

```php
use Pretext\Measurement\FontMeasurerInterface;

class MyFontMeasurer implements FontMeasurerInterface
{
public function measureText(string $text, string $font): float
{
// Your custom measurement logic
return $width;
}

public function setFont(string $font): void
{
// Set current font
}
}

$pretext = new Pretext(new MyFontMeasurer());
```

### Internationalization

```php
// Set locale for proper word segmentation
$pretext->setLocale('th'); // Thai
$thaiText = $pretext->prepare('ภาษาไทย', '16px Arial');

// Reset to default
$pretext->setLocale(null);
```

## API Reference

### Pretext Class

- `prepare(string $text, string $font): PreparedText` - Prepare text for layout
- `prepareWithSegments(string $text, string $font): PreparedTextWithSegments` - Prepare with segment details
- `layout(PreparedText $prepared, float $maxWidth, float $lineHeight): LayoutResult` - Calculate line count and height
- `layoutWithLines(...): LayoutLinesResult` - Layout with per-line details
- `walkLineRanges(...): int` - Walk line ranges without materializing text
- `layoutNextLine(...): ?LayoutLine` - Get next line from cursor position
- `clearCache(): void` - Clear internal caches
- `setLocale(?string $locale): void` - Set locale for word segmentation

### PretextFactory Class

Static factory providing convenient access to all Pretext functionality.

### Font Measurers

- `StubFontMeasurer` - Testing/development (constant character widths)
- `GdFontMeasurer` - Production (uses GD/FreeType with actual font files)

## Architecture

The library follows a layered architecture:

```
Pretext (Public API)

Analysis (Text segmentation & normalization)

Bidi (Bidirectional text algorithm)

Measurement (Text measurement via injectable interface)

LineBreak (Line breaking algorithm)
```

## Differences from TypeScript Original

1. **Font measurement** - File-based (GD/FreeType) instead of browser Canvas
2. **Emoji correction** - Always 0 (no DOM comparison available)
3. **EngineProfile** - Always uses server/headless profile (no browser detection)
4. **Caching** - Per-request by default (no shared memory)
5. **Bidi segment starts** - Codepoint-indexed (cleaner, all bidi ranges are BMP)

## Testing

```bash
# Install dependencies
composer install

# Run tests
./vendor/bin/phpunit

# Run with coverage
./vendor/bin/phpunit --coverage-html coverage
```

## License

MIT

## Credits

Based on the original TypeScript implementation by Sebastian Markbage and contributors.