Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Perfex Pdf

ASecurity

Use whenever the user is customizing, overriding, or debugging PDF output in Perfex CRM — invoice PDFs, estimate PDFs, proposal PDFs, payment receipts, contract PDFs, statement PDFs, credit note PDFs, the `my_` prefix override convention, TCPDF library usage, `App_items_table` customization, font configuration (freesans, dejavusans, droidsansfallback), PDF merge fields, logo/heading settings, or e-invoice JSON/XML export (3.4.0+). Also trigger when the user says "my PDF is blank", "Arabic tex...

3 stars
0 votes
0 copies
0 views
Added 9/19/2026
ai-agentsgophpawsdebuggingapidocumentation

Works with

cliapi

Security Analysis

A100/100

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add yasserstudio/perfex-crm-skills --skill perfex-pdf --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Perfex Pdf?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Perfex Pdf
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/yasserstudio-perfex-pdf/badge)](https://www.skillsdirectory.com/skills/yasserstudio-perfex-pdf)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: perfex-pdf
description: Use whenever the user is customizing, overriding, or debugging PDF output in Perfex CRM — invoice PDFs, estimate PDFs, proposal PDFs, payment receipts, contract PDFs, statement PDFs, credit note PDFs, the `my_` prefix override convention, TCPDF library usage, `App_items_table` customization, font configuration (freesans, dejavusans, droidsansfallback), PDF merge fields, logo/heading settings, or e-invoice JSON/XML export (3.4.0+). Also trigger when the user says "my PDF is blank", "Arabic text broken in PDF", "custom logo not showing in invoice PDF", "how to add a field to the invoice PDF", "PDF font wrong", "override invoicepdf.php", "items table column in PDF", or "e-invoice XML format".
license: MIT
metadata:
  author: yasserstudio
  version: "1.5.0"
---

# Perfex PDF Customization

You are a Perfex CRM PDF engineer. Your job is to customize PDF templates — invoice, estimate, proposal, contract, payment, statement, credit note — using TCPDF correctly, the `my_` prefix override convention, proper font selection for multi-language support, and the `App_items_table` class for line-item formatting.

Perfex generates PDFs via the TCPDF library. Templates live in `application/views/themes/perfex/views/` and are plain PHP files that call TCPDF methods on a `$pdf` object.

## Template file locations

| Document | Core file | Override file |
|---|---|---|
| Invoice | `invoicepdf.php` | `my_invoicepdf.php` |
| Estimate | `estimatepdf.php` | `my_estimatepdf.php` |
| Proposal | `proposalpdf.php` | `my_proposalpdf.php` |
| Payment receipt | `paymentpdf.php` | `my_paymentpdf.php` |
| Contract | `contractpdf.php` | `my_contractpdf.php` |
| Statement | `statementpdf.php` | `my_statementpdf.php` |
| Credit note | `credit_note_pdf.php` | `my_credit_note_pdf.php` |

All located in: `application/views/themes/perfex/views/`

## The `my_` prefix override convention

To customize a PDF without touching core files:

1. Copy the core template (e.g., `invoicepdf.php`)
2. Rename with `my_` prefix: `my_invoicepdf.php`
3. Place in the same directory: `application/views/themes/perfex/views/`
4. Edit the `my_` version

Perfex checks for the `my_` prefixed version first. This survives core updates — the only risk is if Perfex makes "huge changes" to the template structure in a major release.

This is not PDF-specific: `App_Loader` applies the `my_` lookup to **every** core view (`my_invoicehtml.php`, `my_dashboard.php`…), and to module views when the module calls `add_module_support($module, 'my_prefixed_view_files')`. See `perfex-theme` and `perfex-module-dev`.

## TCPDF basics

Templates receive a `$pdf` object (TCPDF instance). Common methods:

```php
// Set font
$pdf->SetFont('freesans', '', 10);

// Add content
$pdf->writeHTML($html, true, false, true, false, '');

// Add a new page
$pdf->AddPage();

// Set margins
$pdf->SetMargins(15, 15, 15);

// Cell (x, y positioned text)
$pdf->Cell(0, 10, 'Text here', 0, 1, 'L');

// Multi-cell (wrapping text)
$pdf->MultiCell(0, 10, $long_text, 0, 'L');

// Image
$pdf->Image($logo_path, 15, 15, 40);
```

Full TCPDF docs: https://tcpdf.org/docs/srcdoc/TCPDF/class-TCPDF/

## Font selection

| Language/Script | Font | Notes |
|---|---|---|
| Latin, Cyrillic | `freesans` | Default, UTF-8 support |
| Arabic | `dejavusans` | Also: `aealarabiya`, `aefurat` |
| Japanese, Chinese | `droidsansfallback` | CJK characters |

Configure default font at **Setup → Settings → PDF**.

```php
// In your my_invoicepdf.php — force Arabic font
$pdf->SetFont('dejavusans', '', 10);
```

If the PDF shows boxes or blank chars, the font doesn't cover the character set. Switch to the appropriate font above. The Setup → Settings → PDF dropdown is built by `get_pdf_fonts_list()`, which scans TCPDF's own font directory — only fonts that exist there are selectable.

### Custom / brand fonts — register before `setFont()`, and only what you use

Two traps, both from production:

1. **TCPDF's font directory is inside `application/vendor/`, which a Perfex upgrade replaces.** A font you drop in there disappears on the next update and every PDF dies with `Could not include font definition file`. Keep custom fonts under your theme/module and register them with a full path: `$pdf->AddFont('brandsans', '', FCPATH . 'assets/themes/my_theme/fonts/brandsans.php')`.
2. **The `pdf_construct` hook fires *after* the default `setFont()`.** `App_pdf::__construct()` calls `setFont(get_option('pdf_font'))` first, then `do_action('pdf_construct', ['pdf_instance' => $pdf, 'type' => $type])`. So if the *default* PDF font is a custom one, registering it in `pdf_construct` is too late — the constructor has already fataled. `pdf_construct` is fine for fonts a template switches to later; for a custom default font, either extend `App_pdf` or keep the default a stock font and `SetFont()` your brand font at the top of `my_invoicepdf.php`.

Register only the family a document actually draws with. TCPDF embeds every registered font whether or not it's used — registering a whole brand family put ~90 KB of unused weights on every invoice.

### PDF lifecycle hooks

| Hook | Fires | Payload |
|---|---|---|
| `pdf_construct` | after default font/margins set | `['pdf_instance' => App_pdf, 'type' => 'invoice'…]` |
| `pdf_header` | inside `Header()` | same |
| `pdf_footer` | inside `Footer()` | same |
| `pdf_close` | before output | same |
| `<type>_pdf_build_path` (filter) | when building the file path for attachments | path string |

All actions receive one array — no `accepted_args` needed.

## Adding custom data to a PDF template

Inside `my_invoicepdf.php`, you have access to the full invoice object. To add a custom field:

```php
// Read a custom field value
$passport = get_custom_field_value($invoice->id, 'invoice_passport_number', 'invoice');

// Or query directly
$CI =& get_instance();
$CI->db->select('v.value');
$CI->db->from(db_prefix() . 'customfieldsvalues v');
$CI->db->join(db_prefix() . 'customfields f', 'f.id = v.fieldid');
$CI->db->where('v.relid', $invoice->clientid);
$CI->db->where('f.slug', 'mymodule_tax_id');
$row = $CI->db->get()->row();
$tax_id = $row ? $row->value : '';

// Render it
$pdf->SetFont('freesans', 'B', 9);
$pdf->Cell(0, 5, 'Tax ID: ' . $tax_id, 0, 1, 'L');
```

## Items table customization (`App_items_table`)

The line-items table (qty, description, rate, tax, amount) is rendered by `application/libraries/App_items_table.php`, which extends the abstract `App_items_table_template`. It handles **both** HTML preview and PDF output; `$for` is `'html'` or `'pdf'`.

There is **no column-config filter** (no `items_table_columns`). The supported extension point is the `items_table_class` filter: return your own subclass and override the three abstract methods — `items()`, `html_headings()`, `pdf_headings()`.

```php
// modules/my_module/libraries/My_items_table.php
class My_items_table extends App_items_table
{
    public function pdf_headings()
    {
        // Return the <tr> of <th> cells for the PDF. Copy the parent
        // implementation and add/remove columns; widths are percentages.
        return parent::pdf_headings();
    }

    public function items()
    {
        // Build each <tr>. $this->items holds the rows; $this->type is
        // 'invoice' | 'estimate' | 'proposal' | 'credit_note'; $this->for is 'html' | 'pdf'.
        return parent::items();
    }
}

// modules/my_module/my_module.php
hooks()->add_filter('items_table_class', function ($class, $transaction, $type, $for, $admin_preview) {
    if ($type !== 'invoice') {
        return $class;                       // leave estimates/proposals alone
    }
    include_once module_libs_path('my_module') . 'My_items_table.php';
    return new My_items_table($transaction, $type, $for, $admin_preview);
}, 10, 5);
```

`get_items_table_data()` in `sales_helper.php` runs this filter and `show_error()`s if what you return isn't an `App_items_table_template`. Smaller tweaks have their own filters: `item_description_td_width` (default `38`, percent), `item_tax_table_row`, `show_tax_per_item`, `items_table_amounts_exclude_currency_symbol`.

Don't copy `App_items_table.php` over the core file — it's overwritten on update and the filter exists precisely so you don't have to.

## PDF heading text

Headings are language strings. Override in `application/language/english/custom_lang.php`:

```php
$lang['invoice_pdf_heading']     = 'TAX INVOICE';
$lang['estimate_pdf_heading']    = 'QUOTATION';
$lang['proposal_pdf_heading']    = 'PROPOSAL';
$lang['credit_note_pdf_heading'] = 'CREDIT NOTE';
```

This respects the customer's language setting — if the customer is set to French, Perfex uses the French translation of these keys.

## Logo configuration

**Setup → Settings → PDF → Custom PDF Company Logo URL**

- If blank, Perfex uses the uploaded company logo from Settings → Company
- Set a URL for a different logo (e.g., higher resolution for print)
- Width is configurable in the same settings panel

## Paper size and orientation

Configured at **Setup → Settings → PDF → Document Formats**. Perfex defaults to A4 portrait. For US Letter or landscape, change here — don't hardcode in templates.

## E-invoice support (Perfex 3.4.0+)

Perfex 3.4.0 added e-invoice compatible output:

- JSON/XML template generators for invoices and credit notes
- Bulk export in JSON/XML formats
- View/download individual invoices as JSON/XML

This is separate from PDF generation — it uses structured data templates, not TCPDF. If you need to customize e-invoice output, look for the JSON/XML template files in the same views directory.

## Multi-language PDF output

Perfex resolves PDF language by:
1. Customer's configured language (profile setting)
2. System default language (fallback)

Admin users can force output in the customer's language via **Setup → Settings → Localization**. This affects merge field labels, headings, and date formats in the PDF.

## Common pitfalls

- **Blank PDF** — usually a PHP fatal error inside the template. Enable `ENVIRONMENT = 'development'` in `index.php` to see the error. TCPDF swallows errors silently in production mode.
- **Logo not showing** — path must be absolute filesystem path for `$pdf->Image()`, not a URL. Use `FCPATH . 'uploads/company/logo.png'`.
- **Arabic text reversed** — TCPDF needs RTL direction set: `$pdf->setRTL(true)` before writing Arabic content. Reset with `$pdf->setRTL(false)` after.
- **Custom CSS ignored** — TCPDF supports a limited subset of HTML/CSS. No flexbox, no grid, no float. Use `<table>` for layout. Inline styles only (`style=""` attributes).
- **Items table changes appear in HTML preview but not the PDF** — your `items_table_class` subclass overrode `html_headings()` but not `pdf_headings()` (or `items()` branches on `$this->for`). Both surfaces come from the same class; override both.
- **`writeHTML` renders corrupted** — HTML must be well-formed XHTML (closed tags, quoted attributes). TCPDF parser is strict. Use `htmlspecialchars()` on user data.

## Related skills

- **`perfex-customfields`** — reading custom field values to display in PDF templates.
- **`perfex-core-apis`** — `_l()` for PDF heading translations, `get_option()` for PDF settings.
- **`perfex-theme`** — client-area invoice HTML preview uses theme views; PDF uses separate templates.
- **`perfex-module-dev`** — modules can register hooks that add data to the PDF context.

## Upstream docs

- Perfex PDF customization: https://help.perfexcrm.com/pdf-customization/
- TCPDF documentation: https://tcpdf.org/docs/srcdoc/TCPDF/class-TCPDF/
- TCPDF examples: https://tcpdf.org/examples/

---

*Verified against Perfex CRM 3.4.0 core source on 2026-09-15 with `scripts/verify-against-core.sh`. Version-specific notes in the text are from official changelogs.*

Attribution

yasserstudioyasserstudio
View sourceMore from yasserstudio →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra, wenyan-lite, wenyan-full, wenyan-ultra. Use when user says "caveman mode", "talk like caveman", "use caveman", "less tokens", "be brief", or invokes /caveman. Also auto-triggers when token efficiency is requested.

1023331 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

686011 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3331 votes

catchup

Recovers prior coding-agent session context by running `catchup <agent> --since-compact`, which extracts a clean summary of a previous Codex, Claude Code, Antigravity, OpenCode, or Pi Agent session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", or asks to recover/summarize a previous session before continuing. Do NOT use for the current conversation, git history, or any non-agent log.

611 votes

math-skill

A comprehensive mathematical reasoning skill for AI assistants — handles arithmetic to research-level problems with rigorous step-by-step reasoning, systematic verification, and transparent uncertainty handling

381 votes
View all in ai-agents →