TCPDF + FPDI : Ensure that all fonts are fully embedded - pdf
I'm using TCPDF + FPDI. I want to embed all fonts fully. Font embedding works great when using addTTFfont() and SetFont() methods. But when I try to open existing pdf file using FPDI, I can't get list of used fonts and can't determine which of them are already embedded. Here is what I'm trying to do:
Get the font info(name, type, etc) of source pdf file;
Determine which fonts are not fully embedded;
Embed those fonts.
So far I tried to make TCPDF class protected member fonts public. So, I changed tcpdf.php file:
/**
* Array of used fonts.
* #protected
*/
public $fonts = array();
In main.php
$pdf = new FPDI();
$pdf->setSourceFile('Simple.pdf');
$pdf->AddPage();
$tplIdx = $pdf->importPage(1);
$pdf->useTemplate($tplIdx, 10, 10, 200);
$pdf->setFontSubsetting(false);
var_dump($pdf->fonts);
Output:
array (size=2)
'helvetica' =>
array (size=17)
'fontkey' => string 'helvetica' (length=9)
'i' => int 1
'type' => string 'core' (length=4)
'name' => string 'Helvetica' (length=9)
'desc' =>
array (size=13)
'Flags' => int 32
'FontBBox' => string '[-166 -225 1000 931]' (length=20)
'ItalicAngle' => int 0
'Ascent' => int 931
'Descent' => int -225
'Leading' => int 0
'CapHeight' => int 718
'XHeight' => int 523
'StemV' => int 88
'StemH' => int 76
'AvgWidth' => int 513
'MaxWidth' => int 1015
'MissingWidth' => int 513
'up' => int -100
'ut' => int 50
'cw' =>
array (size=256)
0 => int 500
1 => int 500
2 => int 500
...
...
'cbbox' =>
array (size=0)
empty
'dw' => int 513
'enc' => string '' (length=0)
'cidinfo' =>
array (size=4)
'Registry' => string 'Adobe' (length=5)
'Ordering' => string 'Identity' (length=8)
'Supplement' => int 0
'uni2cid' =>
array (size=0)
empty
'file' => string '' (length=0)
'ctg' => string '' (length=0)
'subset' => boolean false
'subsetchars' =>
array (size=255)
0 => boolean true
1 => boolean true
2 => boolean true
3 => boolean true
...
...
'n' => int 3
'helveticaB' =>
array (size=17)
'fontkey' => string 'helveticaB' (length=10)
'i' => int 2
'type' => string 'core' (length=4)
'name' => string 'Helvetica-Bold' (length=14)
'desc' =>
array (size=13)
'Flags' => int 32
'FontBBox' => string '[-170 -228 1003 962]' (length=20)
'ItalicAngle' => int 0
'Ascent' => int 962
'Descent' => int -228
'Leading' => int 0
'CapHeight' => int 718
'XHeight' => int 532
'StemV' => int 140
'StemH' => int 118
'AvgWidth' => int 535
'MaxWidth' => int 1000
'MissingWidth' => int 535
'up' => int -100
'ut' => int 50
'cw' =>
array (size=256)
0 => int 278
1 => int 278
2 => int 278
3 => int 278
...
...
'cbbox' =>
array (size=0)
empty
'dw' => int 535
'enc' => string '' (length=0)
'cidinfo' =>
array (size=4)
'Registry' => string 'Adobe' (length=5)
'Ordering' => string 'Identity' (length=8)
'Supplement' => int 0
'uni2cid' =>
array (size=0)
empty
'file' => string '' (length=0)
'ctg' => string '' (length=0)
'subset' => boolean false
'subsetchars' =>
array (size=255)
0 => boolean true
1 => boolean true
2 => boolean true
3 => boolean true
...
...
'n' => int 5
There are info about 2 fonts: "helvetica"(which is the default font according to TCPDF documentacion) and "helveticaB". But source file contains other fonts too. They are not listed. How to list them?
Thanks in advance...
You will need to ensure that the imported document/pages have all fonts embedded in advance. It is impossible to ensure that fonts in imported pages are embedded through FPDI.
You also cannot access/list the fonts with FPDI.
After digging in .pdf file and reading some topics of pdf documentation, I managed to somehow solve the problem. The following code is only draft and needs a lot of improvements. I've tested several .pdf files and it worked, but there are a lot of things, that need a better approach.
While trying to solve the problem, I decided to leave TCPDF and FPDI libraries unchanged. I haven't used any method of those libraries, since I don't know the inner functionality of those libraries.
P.S: Unfortunately, I didn't have much time and my code is long (and not stable):
<?php
require_once('tcpdf.php');
require_once('fpdi.php');
class PDF extends FPDI
{
var $_tplIdx;
private $objects;
private $font_list;
private $raw_data;
private $raw_data_len;
private $font_read_offset;
private $embedded_font_addr;
private $is_descendant;
function __construct($filename)
{
$this->objects = array();
$this->font_list = array();
$this->raw_data = file_get_contents($filename);
$this->raw_data_len = strlen($this->raw_data);
$this->font_read_offset = 0;
$this->is_descendant = false;
parent::__construct();
}
private function EmbeddedFontRefs()
{
do
{
$f = $this->GetXObj('Subtype /CIDFontType2');
if($f == NULL)
return;
//var_dump($f);
$keys = array_keys($f);
$f = $f[$keys[0]];
$name = '';
$ref = '';
if(preg_match('/Subtype \/CIDFontType2/', $f))
{
$matches = array();
if(preg_match('/BaseFont \/\S+/', $f, $matches))
$name = substr($matches[0], 10);
if(preg_match('/FontDescriptor \d+ \d+ R+/', $f, $matches))
{
$ref = substr($matches[0], 15);
$ref = $this->GetObjFromRef($ref);
}
$this->embedded_font_addr[$name] = $ref;
}
}while($f !== NULL);
}
private function ParseObj($obj_search_string, $referrer_object_addr=NULL, $referrer_object_name=NULL)
{
$object_data = $this->GetObj($obj_search_string);
if($object_data == NULL)
return false;
$object_addr = array_keys($object_data);
$object_addr = $object_addr[0];
$object_raw_data = $object_data[$object_addr];
$embedded = false;
$subset = false;
if(!array_key_exists($object_addr, $this->objects))
{
$this->objects[$object_addr] = array();
$obj_content_items = preg_split('/\n\r?/', $object_raw_data);
for($i = 0; $i < count($obj_content_items); $i++)
{
$item = explode(' ', $obj_content_items[$i], 2);
if($item[0][0] == '/')
$item[0] = substr($item[0], 1);
if($item[1][0] == '/')
$item[1] = substr($item[1], 1);
if(($i + 1) < count($obj_content_items))
$item[1] = substr($item[1], 0, -1);
if(($item[0] == 'FontFile') || ($item[0] == 'FontFile2') || ($item[0] == 'FontFile3'))
$embedded = true;
else if(($item[0] == 'BaseFont') || ($item[0] == 'FontName'))
{
$item[1] = str_replace('#20', ' ', $item[1]);
if(preg_match('/[A-Z]{5}\+/', $item[1]))
$subset = true;
}
$this->objects[$object_addr][$item[0]] = $item[1]; // item_name => item_value
}
}
if($referrer_object_addr == NULL)
{
if(array_key_exists('Subtype', $this->objects[$object_addr]))
{
if($this->objects[$object_addr]['Subtype'] == 'Type0') //Composite Fonts
{
if(array_key_exists('DescendantFonts', $this->objects[$object_addr]))
{
$descendant_fonts_obj_addr = $this->GetObjFromRef($this->objects[$object_addr]['DescendantFonts']);
array_push($this->font_list, '');
$this->font_list[count($this->font_list)-1] = $this->objects[$object_addr];
$this->ParseObj($descendant_fonts_obj_addr, $object_addr, 'DescendantFonts');
}
}
}
if(array_key_exists('FontDescriptor', $this->objects[$object_addr]))
{
$font_desc_obj_addr = $this->GetObjFromRef($this->objects[$object_addr]['FontDescriptor']);
array_push($this->font_list, '');
$this->font_list[count($this->font_list)-1] = $this->objects[$object_addr];
$this->ParseObj($font_desc_obj_addr, $object_addr, 'FontDescriptor');
}
}
else
{
if(array_key_exists($referrer_object_addr, $this->objects))
{
if($referrer_object_name == 'DescendantFonts')
{
$this->font_list[count($this->font_list)-1]['DescendantFonts'] = array();
array_push($this->font_list[count($this->font_list)-1]['DescendantFonts'], $this->objects[$object_addr]);
$this->is_descendant = true;
if(array_key_exists('FontDescriptor', $this->objects[$object_addr]))
{
$font_desc_obj_addr = $this->GetObjFromRef($this->objects[$object_addr]['FontDescriptor']);
$this->ParseObj($font_desc_obj_addr, $object_addr, 'FontDescriptor');
}
}
else if($referrer_object_name == 'FontDescriptor')
{
if(array_key_exists('Type', $this->objects[$object_addr]))
unset($this->objects[$object_addr]['Type']);
//Embedded or not?
$keys = array_keys($this->objects[$object_addr]);
foreach($keys as $key)
{
if(($key == 'FontFile') || ($key == 'FontFile2') || ($key == 'FontFile3'))
{
$embedded = true;
break;
}
}
if(!$this->is_descendant)
{
$this->font_list[count($this->font_list)-1]['Embedded'] = $embedded;
$this->font_list[count($this->font_list)-1]['Subset'] = $subset;
$this->font_list[count($this->font_list)-1]['FontDescriptor'] = $this->objects[$object_addr];
}
else
{
$this->font_list[count($this->font_list)-1]['DescendantFonts'][0]['Embedded'] = $embedded;
$this->font_list[count($this->font_list)-1]['DescendantFonts'][0]['Subset'] = $subset;
$this->font_list[count($this->font_list)-1]['DescendantFonts'][0]['FontDescriptor'] = $this->objects[$object_addr];
$this->is_descendant = false;
}
}
}
}
return true;
}
private function GetObj($obj_search_string)
{
$obj_offset = strpos($this->raw_data, $obj_search_string, $this->font_read_offset);
if($obj_offset == false)
{
return NULL;
}
$obj_start = 0;
$obj_end = 0;
$object_content_start = 0;
$object_content_end = 0;
$obj_start = strrpos($this->raw_data, 'endobj', $obj_offset - $this->raw_data_len) + 8; // for 'endobj\n\r';
$obj_end = strpos($this->raw_data, 'endobj', $obj_start);
$this->font_read_offset = $obj_end;
$object_content_start = strpos($this->raw_data, '<<', $obj_start) + 2; //for '<<'
$object_content_end = strpos($this->raw_data, '>>', $object_content_start) - 2; //for /n/r;
$object_addr = substr($this->raw_data, $obj_start, $object_content_start - $obj_start - 4); // -2 for /n/r;
$object_raw_data = substr($this->raw_data, $object_content_start, $object_content_end - $object_content_start);
return array($object_addr => $object_raw_data);
}
private function GetXObj($obj_search_string)
{
$obj_offset = strpos($this->raw_data, $obj_search_string, $this->font_read_offset);
if($obj_offset == false)
return NULL;
$obj_start = 0;
$obj_end = 0;
$object_content_start = 0;
$object_content_end = 0;
$obj_start = strrpos($this->raw_data, 'endobj', $obj_offset - $this->raw_data_len) + 8; // for 'endobj\n\r';
$obj_end = strpos($this->raw_data, 'endobj', $obj_start);
$this->font_read_offset = $obj_end;
$object_content_start = strpos($this->raw_data, '<<', $obj_start) + 2; //for '<<'
$object_content_end = $obj_end - 1;
$object_addr = substr($this->raw_data, $obj_start, $object_content_start - $obj_start - 3); // -2 for /n/r;
$object_raw_data = substr($this->raw_data, $object_content_start, $object_content_end - $object_content_start);
return array($object_addr => $object_raw_data);
}
private function GetFontDescriptor($font_object_addr)
{
if(array_key_exists('FontDescriptor', $this->objects[$font_object_addr]))
$font_desc_ref = $this->objects[$font_object_addr]['FontDescriptor'];
$font_desc_obj_addr = $this->GetObjFromRef($font_desc_ref);
$this->ParseObj($font_desc_obj_addr, $font_object_addr);
}
}
private function GetObjFromRef($obj_ref)
{
$obj_ref_num = explode(' ', $obj_ref);
$obj = '';
if(end($obj_ref_num) == 'R')
{
foreach($obj_ref_num as $num)
{
if($num != 'R')
$obj .= (string)$num. ' ';
else
$obj .= 'obj';
}
}
else if(end($obj_ref_num) == 'obj')
$obj = $obj_ref;
return $obj;
}
public function EnsureFonts($filename)
{
$this->raw_data = file_get_contents($filename);
$this->raw_data_len = strlen($this->raw_data);
$this->font_read_offset = 0;
$this->EmbeddedFontRefs();
$this->font_read_offset = 0;
$font_objs = array();
do
{
$xobj = $this->GetXObj('/Type /XObject');
if($xobj !== NULL)
{
$xobj_addr = array_keys($xobj);
$xobj_addr = $xobj_addr[0];
$matches = array();
if(preg_match('/Resources \d+ \d+ R/', $xobj[$xobj_addr], $matches))
{
array_push($font_objs, $this->GetObjFromRef(substr($matches[0], 10))); //Resources+' '
}
}
}while($xobj !== NULL);
$fxobjs = array();
foreach($font_objs as $font_obj)
{
$fobj = $this->GetXObj($font_obj);
if($fobj !== NULL)
{
$xobj_addr = array_keys($fobj);
$xobj_addr = $xobj_addr[0];
$matches = array();
if(preg_match('/Font \d+ \d+ R/', $fobj[$xobj_addr], $matches))
{
array_push($fxobjs, $this->GetObjFromRef(substr($matches[0], 10))); //Resources+' '
}
}
}
$actual_font_objs = array();
foreach($fxobjs as $fxobj)
{
$f_obj = $this->GetXObj($fxobj);
if($f_obj !== NULL)
{
$xobj_addr = array_keys($f_obj);
$xobj_addr = $xobj_addr[0];
$matches = array();
if(preg_match('/F\d+/', $f_obj[$xobj_addr], $matches))
{
array_push($actual_font_objs, $this->GetObjFromRef(substr($matches[0], 10))); //Resources+' '
}
}
}
foreach($actual_font_objs as $fo)
{
$f = $this->GetXObj($fxobj);
if($f !== NULL)
{
$xobj_addr = array_keys($f);
$xobj_addr = $xobj_addr[0];
$matches = array();
$f_name = '';
if(preg_match('/BaseFont \/\S+/', $f[$xobj_addr], $matches))
{
$f_name = substr($matches[0], 10); // BaseFont+' '+'/'
}
if(preg_match('/FontDescriptor \d+ \d+ R/', $f[$xobj_addr], $matches))
{
$fd_replace_start_pos = strpos($this->raw_data, $matches[0]) + 15; //FontDescriptor+' ';
$fd_replace_end_pos = strpos($this->raw_data, 'R', $fd_replace_start_pos) + 1; //For 'R';
$s = substr($this->raw_data, 0, $fd_replace_start_pos);
$r = substr($this->embedded_font_addr[$f_name], 0, -3).'R'; //-4 for obj.
$e = substr($this->raw_data, $fd_replace_end_pos);
$this->raw_data = $s . $r . $e;
}
}
}
file_put_contents($filename, $this->raw_data);
}
public function GetFontInfo()
{
do
{
$this->ParseObj('/Type /Font');
}while($this->ParseObj('/Type /Font') == true);
return $this->GetNotEmbeddedFonts();
}
private function GetNotEmbeddedFonts()
{
$result = array();
foreach($this->font_list as $font)
{
if(array_key_exists('DescendantFonts', $font))
{
foreach($font['DescendantFonts'] as $desc_font)
{
if((array_key_exists('Embedded', $desc_font)) && (array_key_exists('BaseFont', $desc_font)))
if(!$desc_font['Embedded'])
array_push($result, $desc_font['BaseFont']);
}
}
else
{
if((array_key_exists('Embedded', $font)) && (array_key_exists('BaseFont', $font)))
if(!$font['Embedded'])
array_push($result, $font['BaseFont']);
}
}
for($i= 0; $i<count($result); $i++)
{
if(strpos($result[$i], ' ') || strpos($result[$i], ',')) //Adobe Acrobat system font name list. Needs improvment..
{
switch($r)
{
case 'Times New Roman':
$result[$i] = 'TimesNewRomanPSMT';
break;
case 'Times New Roman,Bold':
$result[$i] = 'TimesNewRomanPS-BoldMT';
break;
case 'Times New Roman,Italic':
$result[$i] = 'TimesNewRomanPS-ItalicMT';
break;
case 'Times New Roman,BoldItalic':
$result[$i] = 'TimesNewRomanPS-BoldItalicMT';
break;
case 'Aial':
$result[$i] = 'ArialMT';
break;
}
}
}
return $result;
}
}
$pdf = new PDF($filename);
$pdf->setSourceFile($filename);
$pdf->AddPage();
$tplIdx = $pdf->importPage(1);
$pdf->useTemplate($tplIdx);
$pdf->SetMargins(PDF_MARGIN_LEFT, 40, PDF_MARGIN_RIGHT);
$pdf->SetAutoPageBreak(true, 40);
$pdf->setFontSubsetting(false);
$not_embedded_fonts = $pdf->GetFontInfo();
foreach($not_embedded_fonts as $f)
{
$font_name = strtolower($f);
$font_name = preg_replace('/[^a-z0-9_]/', '', $font_name);
$search = array('bold', 'oblique', 'italic', 'regular');
$replace = array('b', 'i', 'i', '');
$font_name = str_replace($search, $replace, $font_name);
if($pdf->AddFont($font_name) !== false)
$pdf->SetFont($font_name);
else
print('"' .$font_name. '" font not found!');
}
$pdf->Output();
?>
Related
What is the fastest way to create a union of many polygons in turfjs?
I have something like this but with large sets it's awfully slow: let unionize = (triangles) => { if(triangles.length == 0) { return null } let ret = triangles[0].feature triangles.forEach((t, index) => { if(index > 0) { ret = turf.union(t, t) } }) return ret }
featureEach tends to be faster than a trivial array forEach although with more features the gain decreases. <script src="https://cdn.jsdelivr.net/npm/#turf/turf#5.1.6/turf.min.js"></script> <script> const generateGrid = (cellSide = 100, units = "kilometers") => { const bbox = [-95, 30, -85, 40]; return turf.triangleGrid(bbox, cellSide, { units }); } const unionizeForEach = (triangles) => { console.time('unionizeForEach'); if (triangles.length == 0) { return null } let ret = triangles[0]; triangles.forEach((t, index) => { if (index > 0) { ret = turf.union(ret, t) } }); console.timeEnd('unionizeForEach'); return ret } const unionizeFeatureEach = (triangles) => { console.time('unionizeFeatureEach'); if (triangles.features.length === 0) { return null } let ret = triangles.features[0]; turf.featureEach(triangles, function (currentFeature, featureIndex) { if (featureIndex > 0) { ret = turf.union(ret, currentFeature) } }); console.timeEnd('unionizeFeatureEach'); return ret; } [100, 50, 10].map(cellSide => { const triangleGrid = generateGrid(cellSide, 'kilometers'); console.info(`triangleGrid has ${triangleGrid.features.length} features`); const union_foreach = unionizeForEach(triangleGrid.features); const union_featureeach = unionizeFeatureEach(triangleGrid); console.assert(JSON.stringify(union_foreach) === JSON.stringify(union_featureeach)); console.log('\n---\n'); }) </script>
Is it possible to shorten this query
we have used loop for read between timestamps. This works but this way exhausting for server with million records. The timestamps are determined by the $resolution variable and assigned to a new array. But we know this is poor way. I think we can do that without while. Maybe one query enough. Route::get('/history', function (Request $request) { //return response()->json([]); $symbol = explode("-", $request->get("symbol")); $coin = Coin::where("symbol", $symbol[1])->first(); $currency = Coin::where("symbol", $symbol[0])->first(); $resolution = $request->get("resolution"); $from = $request->get("from"); $to = $request->get("to"); $uniqueKey = sprintf("OHLCKEY%s%s%s%s", $symbol[0], $symbol[1], Carbon::createFromTimestamp($from)->second(0)->timestamp, Carbon::createFromTimestamp($to)->second(0)->timestamp); $redis=Redis::connection(); $cache = $redis->get($uniqueKey); if($cache) { $data = json_decode($cache); return response()->json($data); } else { $t = []; $o = []; $h = []; $l = []; $c = []; $v = []; $s = "no_data"; $minMaxDates = TransactionDetail::where("type", "buy") ->whereBetween("created_at", [ Carbon::createFromTimestamp($from), Carbon::createFromTimestamp($to) ])->where("coin_id", $coin->id)->where("currency_id", $currency->id) ->selectRaw("min(created_at) as minDate, max(created_at) as maxDate")->first(); if($minMaxDates->minDate) { $minDate = Carbon::createFromTimeString($minMaxDates->minDate)->timestamp; if($from < $minDate) { $startDate = $minDate; } else { $startDate = (int)$from; } $maxDate = Carbon::createFromTimeString($minMaxDates->maxDate)->timestamp; if($to > $maxDate) { $to = $maxDate; } while ($startDate < $to): switch ($resolution) { case "30"; $endDate = Carbon::createFromTimestamp($startDate)->addMinutes(30)->timestamp; break; case "60"; $endDate = Carbon::createFromTimestamp($startDate)->addHour(1)->timestamp; break; case "180"; $endDate = Carbon::createFromTimestamp($startDate)->addHour(3)->timestamp; break; case "360"; $endDate = Carbon::createFromTimestamp($startDate)->addHour(6)->timestamp; break; case "720"; $endDate = Carbon::createFromTimestamp($startDate)->addHour(12)->timestamp; break; default: $endDate = Carbon::createFromTimestamp($startDate)->addHour(1)->timestamp; break; } if ($endDate > $to) $endDate = $to; $transactions = TransactionDetail::where("type", "buy") ->whereBetween("created_at", [ Carbon::createFromTimestamp($startDate), Carbon::createFromTimestamp($endDate) ])->where("coin_id", $coin->id)->where("currency_id", $currency->id)->get(); if (!$transactions->isEmpty()) { $t[] = $transactions->first()->created_at->timestamp; $o[] = floatval($transactions->first()->price); $h[] = floatval($transactions->max("price")); $l[] = floatval($transactions->min("price")); $c[] = floatval($transactions->last()->price); $v[] = floatval($transactions->sum("amount")); } $startDate = $endDate + 1; endwhile; } if (!empty($t)) $s = "ok"; $data = ["t" => $t, "o" => $o, "h" => $h, "l" => $l, "c" => $c, "v" => $v, "s" => $s]; $redis->set($uniqueKey, json_encode($data)); return response()->json($data); } }); Can we reproduce this code with one mysql query? thank you in advance.
Segmentation fault in the below program
The below code is being called from a simple script like this. $test.line-validation(); method line-validation is rw { my $file-data = slurp($!FileName, enc => "iso-8859-1"); my #lines = $file-data.lines; my $start = now; for #lines -> $line { state $i = 1; my #splitLine = split('|', $line); if ($line.starts-with("H|") || $line.starts-with("T|")) { next; } my $lnObject = LineValidation.new( line => $line, FileType => $.FileType ); $lnObject.ColumnIds = %.ColumnIds; my #promises; my #validationIds; for %.ValidationRules.keys -> $validationId { if (%.ValidationRules{$validationId}<ValidationType> eq 'COLUMN') { push #promises, start {$lnObject.ColumnValidationFunction(%.ValidationRules{$validationId}<ValidationFunction>, %.ValidationRules{$validationId}<Arguments>, $.ValidationRules{$validationId}<Description>); 1}; push #validationIds, $validationId; } } my #promise-output = await #promises; for #validationIds -> $valId { state $j = 0; my $result = #promise-output[$j]; if ($result.Bool == True) { if (%.ResultSet{$valId}<count> :!exists) { %.ResultSet{$valId}<count> = 1; } else { %.ResultSet{$valId}<count> = %.ResultSet{$valId}<count> + 1; } my #prCol = (%.ValidationRules{$valId}<Arguments><column>, #.printColumns); if (%.ResultSet{$valId}<count> <= 10) { %.ResultSet{$valId}.push: (sample => join('|', #splitLine[#prCol[*;*]].map: { if ($_.Bool == False ) { $_ = ''} else {$_ = $_;} })); } %.ResultSet{$valId}<ColumnList> = #prCol[*;*]; } $j++; } $i++; } say "Line validation completed in {now - $start } time for $.lineCount lines"; } The code was working fine earlier but when run using larger files, it just arbitrarily throws the error Segmentation fault and exists. I cannot determine where it is failing either.
How to fetch dynamic table list in MVC and angularJS
I'm getting a list in my angular.js file (EditDeleteItem.js) which I'm making based on the selected table name. The function to send my list is as below:- $scope.SaveTblRecord = function (list) { //alert(JSON.stringify($scope.employeeList)); $scope.FetchTableName(); //var Data = $.param({ TblData: $scope.MyTblDataList }); var itemList = []; angular.forEach(list, function (value, key) { if (list[key].selected) { itemList.push(list[key].selected); } }); $scope.ItemsList = []; $scope.ItemsList = itemList; $http({ method: "Post", url: "/Admin/SaveTblData", data: $scope.ItemsList, }).success(function (data) { $scope.GetTblData($scope.TempName); }).error(function (err) { alert(err.Message); }) };//SaveTblRecord Now in my Controller I want to fetch that list based on the selected table name but I can't do it :- public JsonResult SaveTblData(List<LocationTbl> NewTblList) //Need To Have TableName here instead of LocationTbl so that selected table name list is fetched. { string MyTableName = Convert.ToString(TempData["TableName"]); try { if (NewTblList == null) { return new JsonResult { Data = "Empty Selection", JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } else { using (EBContext db = new EBContext()) { bool results = false; Type tableType = typeof(CourseDesc); switch (MyTableName) { //case "CourseTbl": // for (int i = 0; i < NewTblList.Count; i++) // { // var CtObj = NewTblList[i]; // CourseTbl ct = db.Courses.AsNoTracking().FirstOrDefault(x => x.ID == CtObj.ID); // results = UtilityMethods<CourseTbl, int>.EditEntity(db, CtObj); // } // break; //case "CourseDescTbl": // for (int i = 0; i < NewTblList.Count; i++) // { // var CdtObj = NewTblList[i]; // CourseDesc cd = db.CourseDesc.AsNoTracking().FirstOrDefault(x => x.ID == CdtObj.ID); // results = UtilityMethods<CourseDesc, int>.EditEntity(db, CdtObj); // } // break; //case "CourseSubDesc": // for (int i = 0; i < NewTblList.Count; i++) // { // var CsdObj = NewTblList[i]; // CourseSubDesc csd = db.CourseSubDesc.AsNoTracking().FirstOrDefault(x => x.ID == CsdObj.ID); // results = UtilityMethods<CourseSubDesc, int>.EditEntity(db, CsdObj); // } // break; //case "InternTbl": // for (int i = 0; i < NewTblList.Count; i++) // { // var ItObj = NewTblList[i]; // InternShip It = db.Interns.AsNoTracking().FirstOrDefault(x => x.ID == ItObj.ID); // results = UtilityMethods<InternShip, int>.EditEntity(db, ItObj); // } // break; case "LocationTbl": for (int i = 0; i < NewTblList.Count; i++) { var LtObj = NewTblList[i]; LocationTbl lt = db.Loc.AsNoTracking().FirstOrDefault(x => x.ID == LtObj.ID); results = UtilityMethods<LocationTbl, int>.EditEntity(db, LtObj); } break; } var resultList = new List<object>(); foreach (var item in db.Set(tableType)) { resultList.Add(item); } return new JsonResult { Data = resultList, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } } } catch (Exception ex) { return new JsonResult { Data = ex.Message, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } } I've been searching the internet for a while and found some related links like Link1 and Link2 But I can't find solution to my problem. Please HELP!!
gd library - font size?
I am creating a captcha image (please do not suggest premade ones). It gives you the ability to call $captcha = new captcha(); $captcha->size(30)->getImage(); which sets the font size to 30 and then generates the captcha. my issue is with sizing. How can I work out how wide the image should be? the captcha has 6 characters. I thought I could just do ($size*6)+10 to make the image wide enough + give it a 5px padding on each side (text is centered), but apparently not. Code <?php session_start(); class captcha { private $font = 'monofont.ttf'; private $characters = '23456789bcdfghjkmnpqrstvwxyz'; private $size = 30; private $count = 6; private $colors = array( 'b' => array('r' => 0, 'g' => 0, 'b' => 0), 't' => array('r' => 200, 'g' => 200, 'b' => 200), 'n' => array('r' => 127, 'g' => 127, 'b' => 127) ); function count($count) { $this->count = $count; return $this; } function size($size){ $this->size = $size; return $this; } function characters($characters) { $this->characters = $characters; return $this; } function backgroundColor($red, $green, $blue){ $this->colors['b']['r'] = $red; $this->colors['b']['g'] = $green; $this->colors['b']['b'] = $blue; return $this; } function noiseColor($red, $green, $blue){ $this->colors['n']['r'] = $red; $this->colors['n']['g'] = $green; $this->colors['n']['b'] = $blue; return $this; } function textColor($red, $green, $blue){ $this->colors['t']['r'] = $red; $this->colors['t']['g'] = $green; $this->colors['t']['b'] = $blue; return $this; } function generateCode() { $code = ''; $i = 0; while ($i < $this->count) { $code .= strtoupper(substr($this->characters, mt_rand(0, strlen($this->characters) - 1), 1)); $i++; } return $code; } function getWidth() { return ($this->count * $this->size); } function getHeight() { return $this->size+10; } function getCaptcha() { $code = $this->generateCode(); $this->width = $this->getWidth(); $this->height = $this->getHeight(); $image = #imagecreate($this->width, $this->height) or die('Cannot initialize new GD image stream'); //define colors $tColor = imagecolorallocate($image, $this->colors['t']['r'], $this->colors['t']['g'], $this->colors['t']['b']); $nColor = imagecolorallocate($image, $this->colors['n']['r'], $this->colors['n']['g'], $this->colors['n']['b']); $bColor = imagecolorallocate($image, $this->colors['b']['r'], $this->colors['b']['g'], $this->colors['b']['b']); //fill image imagefill($image, 0, 0, $bColor); /* generate random dots in background */ for ($i = 0; $i < ($this->width * $this->height) / 3; $i++) { imagefilledellipse($image, mt_rand(0, $this->width), mt_rand(0, $this->height), 1, 1, $nColor); } /* generate random lines in background */ for ($i = 0; $i < ($this->width * $this->height) / 150; $i++) { imageline($image, mt_rand(0, $this->width), mt_rand(0, $this->height), mt_rand(0, $this->width), mt_rand(0, $this->height), $nColor); } /* create textbox and add text */ $textbox = imagettfbbox($this->size, 0, $this->font, $code) or die('Error in imagettfbbox function'); $x = ($this->width - $textbox[4]) / 2; $y = ($this->height - $textbox[5]) / 2; imagettftext($image, $this->size, 0, $x, $y, $tColor, $this->font, $code) or die('Error in imagettftext function'); //imagefilter($image, IMG_FILTER_NEGATE); imagefilter($image, IMG_FILTER_SMOOTH, 1); /* output captcha image to browser */ header('Content-Type: image/jpeg'); header('Cache-Control: no-cache, must-revalidate'); imagejpeg($image); imagedestroy($image); $_SESSION['security_code'] = $code; } } $captcha = new captcha(); $captcha->size(30)->count(6)->getCaptcha(); ?>
You can use imagegetttfbbox() to determine the bounding box which can contain your string. But since you're generating a captcha, which means the characters are highly distored, I don't think it'd particularly accurate. It's intended for regular un-altered text.