Skip to content

Commit

Permalink
feat: woff to pdf helper (#782)
Browse files Browse the repository at this point in the history
* feat: woff to pdf helper

* fix: corrected if statement

---------

Co-authored-by: Niclas Norin <[email protected]>
  • Loading branch information
NiclasNorin and Niclas Norin authored Dec 8, 2023
1 parent c75a812 commit 34fed68
Show file tree
Hide file tree
Showing 2 changed files with 260 additions and 13 deletions.
37 changes: 24 additions & 13 deletions library/Api/Pdf/PdfHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Municipio\Api\Pdf;

use Municipio\Helper\Image;
use Municipio\Helper\WoffConverter as WoffConverterHelper;

class PdfHelper
{
Expand All @@ -21,7 +22,7 @@ public function getFonts($styles) {
'post_type' => 'attachment',
'posts_per_page' => -1,
'post_status' => 'inherit',
'post_mime_type' => 'font/ttf'
'post_mime_type' => 'application/font-woff'
);

$customFonts = new \WP_Query($args);
Expand All @@ -32,11 +33,11 @@ public function getFonts($styles) {
foreach ($customFonts->posts as $font) {
if (!empty($font->post_title)) {
if ($font->post_title == $heading['font-family']) {
$heading['src'] = !empty($font->ID) ? wp_get_attachment_url( $font->ID ) : '';
$heading['src'] = $this->convertWOFFToTTF($font->ID);
}

if ($font->post_title == $base['font-family']) {
$base['src'] = !empty($font->ID) ? wp_get_attachment_url( $font->ID ) : '';
$base['src'] = $this->convertWOFFToTTF($font->ID);
}
}
}
Expand All @@ -47,42 +48,52 @@ public function getFonts($styles) {

if (empty($base['src']) && !empty($base['font-family']) && !empty($downloadedFontFiles) && is_array($downloadedFontFiles)) {
$baseUrl = $this->createGoogleFontImport($base['font-family']);
$fontFacesString .= $this->buildFontfaces($baseUrl, $downloadedFontFiles);
$fontFacesString .= $this->buildFontFaces($baseUrl, $downloadedFontFiles);
}

if (empty($heading['src']) && !empty($heading['font-family']) && !empty($downloadedFontFiles) && is_array($downloadedFontFiles)) {
$headingUrl = $this->createGoogleFontImport($heading['font-family']);
$fontFacesString .= $this->buildFontFaces($headingUrl, $downloadedFontFiles);
}
echo '<pre>' . print_r( $base, true ) . '</pre>';
echo '<pre>' . print_r( $fontFacesString, true ) . '</pre>';
echo '<pre>' . print_r( $heading, true ) . '</pre>';
die;

return [
'base' => $base,
'heading' => $heading,
'localGoogleFonts' => $fontFacesString,
];
}

private function convertWOFFToTTF($fontId) {
$fontFile = get_attached_file($fontId);
if (!empty($fontFile)) {
if (!empty($fontFile) && file_exists($fontFile) && mime_content_type($fontFile) == 'application/font-woff') {
WoffConverterHelper::convert($fontFile, str_replace('.woff', '.ttf', $fontFile ));
return str_replace('.woff', '.ttf',wp_get_attachment_url( $fontId ));
}
}

return "";
}

private function buildFontFaces ($url, $downloadedFontFiles) {
$fontFacesString = "";
if (ini_get('allow_url_fopen')) {
$response = wp_remote_get( $url, array( 'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8' ) );

if (ini_get('allow_url_fopen')) {
$response = wp_remote_get( $url, array( 'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8' ) );

if ( is_wp_error( $response ) ) {
return [];
}

$contents = wp_remote_retrieve_body( $response );

if (!empty($contents) && is_string($contents)) {
foreach ($downloadedFontFiles as $key => $fontFile) {
$contents = str_replace($key, $fontFile, $contents);
}

$fontFaces = explode('@font-face', $contents);

foreach ($fontFaces as $fontFace) {
if (!preg_match('/fonts.gstatic/', $fontFace) && preg_match('/src:/', $fontFace)) {
$fontFacesString .= '@font-face ' . $fontFace;
Expand Down
236 changes: 236 additions & 0 deletions library/Helper/WoffConverter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
<?php

namespace Municipio\Helper;

class WoffConverter
{

/**
* Unwraps a WOFF font file and repackages it as an OpenType font and writes it to disk.
* @param string $woffFile absolute path of WOFF file (includes filename)
* @param string $convertPath absolute path to cache converted file (includes filename)
*/
public static function convert($woffFile, $convertPath)
{
if (is_null($woffFile) || is_null($convertPath)) {
WoffConverter::bail("Empty argument: convert($woffFile, $convertPath");
}

if (! function_exists('gzuncompress')) {
bail(__CLASS__ . ' requires gzuncompress().');
}

$debug = false;
$numTables = 0;
$oTDirEntrySize = 16; // bytes
$oTHeaderSize = 12;
$woffDirEntrySize = 20;
$woffHeaderSize = 44;

if (! $fh = fopen($woffFile, "rb")) {
WoffConverter::bail("Couldn't open file $woffFile.");
}

$header = unpack('Nsig/Nflv/Nlen/nntab/nres/Nssize/nmajv/nminv/Nmoff/Nmlen/Nmolen/Nprivo/Nprivl', fread($fh, $woffHeaderSize));

if ($debug) {
foreach ($header as $key => $val) {
printf("%s => 0x%x\n", $key, $val) ;
}
}

foreach ($header as $key => $val) {
switch($key) {
case 'sig':
$sfntVersion = $val;
if ($debug) {
if ($val != 0x774F4646) { // wOFF
if ($debug) echo 'font file not WOFF';
WoffConverter::bail("File is not a valid WOFF font.", $fh);
} else {
if ($debug) echo 'font file cool.';
}
}
break;
case 'flv':
$flavor = $val;
if ($val == 0x00010000) {
if ($debug) echo 'TrueType flavor.';
} else if ($val == 0x4F54544F) {
if ($debug) echo 'CFF flavor.';
} else {
if ($debug) echo 'unknown flavor.';
}
// Use otf for all flavors, makes deriving the cache filename easier later on.
$fileExtension = 'otf';
break;
case 'len':
if ($debug) echo "\nfile size $val bytes. ";
break;
case 'ntab':
if ($debug) echo "\n$val font tables.";
$numTables = $val;
break;
case 'res':
if ($val != 0) {
if ($debug) echo "\nproblem - reserved field != 0.";
WoffConverter::bail("Reserved field has to be 0 (zero).", $fh);
}
break;
case 'ssize':
if ($debug) echo "\ntotal data size $val.";
break;
case 'majv':
if ($debug) echo "\nmajor version $val.";
break;
case 'minv':
if ($debug) echo "\nminor version $val.";
break;
case 'moff':
if ($debug) echo "\nmeta offset $val bytes.";
break;
case 'mlen':
if ($debug) echo "\ncompressed data block $val bytes";
break;
case 'molen':
if ($debug) echo "\nuncompressed data block $val bytes";
break;
case 'privo':
if ($debug) echo "\nprivate data offset $val bytes";
$privateOffset = $val;
break;
case 'privl':
if ($debug) echo "\nprivate data length $val bytes";
$privateLength = $val;
break;
}
}

// check out the private data
if ($debug) {
$whereAt = ftell($fh);
fseek($fh, $privateOffset);
$privateData = fread($fh, $privateLength);
echo "Private data: $privateData";
fseek($fh, $whereAt);
}

// write offset table
if ($debug) echo "\nout file $convertPath";
$ofh = fopen($convertPath, "wb");
if (! $ofh) {
WoffConverter::bail("Couldn't open file $outfile for writing.", $fh);
}
fwrite($ofh, pack('N', $flavor));
fwrite($ofh, pack('n', $numTables));

$maxPower2 = 0;
while (pow(2, $maxPower2) <= $numTables) {
$maxPower2++;
}

$searchRange = $maxPower2 * 16;
$entrySelector = log($searchRange, 2);

fwrite($ofh, pack('n', $searchRange));
fwrite($ofh, pack('n', $entrySelector));
fwrite($ofh, pack('n', $numTables * $searchRange));

$tableDirectorySize = $oTDirEntrySize * $numTables;

$tableData = array();
$tableLength = array();
$currentEOF = $tableDirectorySize + $oTHeaderSize;



// Write table records
for ($i = 0; $i < $numTables; $i++ ) {

$dirEntry = unpack('Ntag/N4', fread($fh, $woffDirEntrySize));
if ($debug) {
printf("\ntag value: %d", $dirEntry['tag']);
printf("offset: 0x%x\n", $dirEntry[1]);
printf("compressed size: 0x%x\n", $dirEntry[2]);
printf("uncompressed size: 0x%x\n", $dirEntry[3]);
printf("checksum: 0x%x\n", $dirEntry[4]);
}
$whereAt = ftell($fh);

if (fseek($fh, $dirEntry[1])) {
WoffConverter::bail("fseek-ing to offset {$dirEntry[1]}", $fh, $ofh);
}

$tableData[$i] = fread($fh, $dirEntry[2]);

fseek($fh, $whereAt);
if ($dirEntry[2] != $dirEntry[3]) {
$tableData[$i] = gzuncompress($tableData[$i]);
$tableLength[$i] = strlen($tableData[$i]);
} else {
$tableLength[$i] = $dirEntry[3];
}

fwrite($ofh, pack('N', $dirEntry['tag'])); //tag
fwrite($ofh, pack('N', $dirEntry[4])); // checksum
fwrite($ofh, pack('N', $currentEOF)); // offset
fwrite($ofh, pack('N', $tableLength[$i])); // length without padding


if ($debug) {
printf("\nwriting table rec for tag: %d\n", $dirEntry['tag']);
printf("\n\t checksum: 0x%x\n", $dirEntry[4]);
printf("\n\t offset: %d", $currentEOF);
printf("\n\t length: %d", $tableLength[$i]);
}

$pad = 0;
if (($tableLength[$i] % 4) != 0) { // pad for 4-byte boundaries
$pad = $tableLength[$i] % 4;
}
$currentEOF += strlen($tableData[$i]) + $pad;
}



$bytesWrote = 0;
// Write table data
for ($i = 0; $i < count($tableData); $i++) {
if ($debug) {
echo "\nfile pointer at: " . ftell($ofh);
echo "\ntablelength = " . $tableLength[$i] . ' strlen of data: ' . strlen($tableData[$i]);
}
$bytesWrote += fwrite($ofh, $tableData[$i], $tableLength[$i]);
if ($debug) echo "\nwriting {$tableLength[$i]} bytes. Actual total bytes written: $bytesWrote";

if (($tableLength[$i] % 4) != 0) { // pad for 4-byte boundaries
$pad = $tableLength[$i] % 4;
$bytesWrote += fwrite(($ofh), pack("x$pad"));
if ($debug) echo "\nwriting $pad null bytes. Actual total bytes written: $bytesWrote";
}
}



fclose($fh);
fclose($ofh);
}


/**
* Throws an exception with $message and closes given file handles if applicable.
* @param string $message
* @param Resource $fh
* @param Resource $ofh
*/
private static function bail($message, $fh = NULL, $ofh = NULL)
{
if (is_resource($fh)) {
close($fh);
}
if (is_resource($ofh)) {
close($ofh);
}
throw new \WP_Error($message);
}
}

0 comments on commit 34fed68

Please sign in to comment.