Ads

Ad main logics

Creating

Ads are created via crop or upload from the article’s edit page from the ads tab.
Every ad belongs to an article and pdf and can have multiple advertisers.
The first advertiser is considered main advertiser and it’s ID is assigned to the ad db record inside advertiser_id field. After being cropped/uploaded the ad is saved into the ads db table.

Currently the code responsible for saving the ads is in app/Http/Livewire/Ads/Tab.php This tab component is being called and rendered in the Article’s Edit Page and contains all the logic for saving a cropped ad and properly updating the article’s ad_num and ad_mm_sum fields.

And for uploading an an the responsible code is in app/Http/Livewire/Articles/Edit@submitAdImage which calls: app/Helpers/AdsHelper@uploadAdImage

Editing

The ads are edited from the ads editor. Every article edit page contains a button which leads to this editor. The ad advertises can be created/updated from the ads editor. Each advertiser is later saved in advertisers table, and for the first one we save it’s id inside ads table. All the consenquent advertisers are saved both inside the advertisers json field inside the ads table and also in the advertisers table. This is implemented in case we wan’t to reuse an adveriser but with slightly diffrent data. So we can autocomplete an advertiser from the UI and only change it’s address or phone for this particular ad and save this data inside the Ad json field instead of directly changing the advertiser and affecting other ads for this advertiser.

Size and Milimiters Calculations

Each ad db record contains the ad’s image width and height in pixels. To determine them we use the builin php function getimagesize. So in order to detect them we need to save the ad image first on the filesystem.
We use width/height in pixels to calculate its width/height in milimiters. The current formulas are:

(widthPixels * 25.4)/pixels_per_inch 
(heightPixels * 25.4)/pixels_per_inch 

The current pixels_per_inch value we use is 150 . It’s defined in: config/processing.php
Other important field for each ad is the col_num field. It describes how much columns the ad is taking in the pdf. This field is later used to calculate it’s full height for invoicing purposes. Usually this field comes from the pdf from which we cropped the ad or is manually provided if the ad image was manually uploaded.

Example php code:

    # Save base64 encoded image to the filesystem
    $image = imageStore($base64EncodedImage);

    # Insert the image data inside the images table
    $image = Image::create([
        'item_id' => $pdf->id,
        'path' => $image->path,
        'name' => $image->name,
        'type' => Image::getType('ad')
    ]);

    $ad = new Ad;
    # Get the image size in pixels
    list($width, $height) = getimagesize($b64Ad);
    # Get the pixels per inch value
    $ppi = config("processing.ftp.ppi");
    $ad->image_id = $image->id;
    $ad->width = $width;
    $ad->height = $height;
    # Calculate the width/height in milimeters
    $ad->width_mm = round($width * 25.4 / $ppi);
    $ad->height_mm = round($height * 25.4 / $ppi);
    $ad->col_num = $colNum;
    $ad->alias = 'ad-'.\Str::uuid();    
    $ad->save()

After saving/deleting an ad is important to update the article as well because we save the ads_count and ad_mm_summ for each article inside the article’s db record for optimization purposes so we don’t have to do the agregation later we fetch statistics and invoicing data.

Example php code:

	\DB::table('articles')->where('id', $article->id)
    ->update([
        'ads_mm_sum' => $article->calculateAdsMMSum(),
        'ads_count'  => $article->ads_count+1
    ]);

Inside \App\Models\Article is the code responsible for counting all of the articles ads milimiters.

    public function calculateAdsMMSum() {
        $ppi = config("processing.ftp.ppi");

        $dimensions = [];
        # Get all of the ads from the database.
        # It's importang to use ads() instead of just $this->ads because in case of eager loading
        # we might miss the newly uploaded ads (ads which was uploaded after the article model was loaded)
        $ads = $this->ads()->get();
        # Calc. dimensions for each ad
        $ads->each(function($ad) use (&$dimensions, $ppi) {
            $heightMM = round($ad->height * 25.4 / $ppi);
            array_push($dimensions, $heightMM * $ad->col_num);
        });

        $totalSum = array_sum($dimensions);
        # The minimum sum of ads_mm is 250, so if the sum is 0 (in case of no ads) or less then 250 we set them to 250
        return $totalSum < 250 ? 250 : $totalSum;
    }

Cropping

The plugin we use for cropping: jquery-select-areas
If the ad is cropped from a pdf we saved it’s coordinates and sizes from the original pdf page image. We need data in order to later crop text from the ad image itself, but since the ad image is not directly procesed in Google Vision we need to calculate the crop areas directly on the original pdf image from which the ad image was cropped.

Example cropped_data contents:

{"x":60,"y":109,"height":73,"width":609,"image":"http://antoan.dev2.transmatico.com/storage/pdfs/1248/pages/page-1.png","clientWidth":753,"clientHeight":1083}

When we have this data and crop text from the ad image we can get the crop selection dimensions and it’s x/y coordinates and recalculate them as they would appear if we made the text crop selection on the original pdf image. Example cropping recalculation:

	$adImage.selectAreas({
		onChanged:  function (event, id, areas) {
            if (!areas.length) {
				return false;
			}
			let ratioW2 = cropData.width/$(this).width();
			let ratioH2 = cropData.height/$(this).height();	
			window.area = {};
			window.area.x = cropData.x + (window.area.x*ratioW2);
			window.area.y = cropData.y + (window.area.y*ratioH2);
			window.area.width = ratioW2 * window.area.width; 
			window.area.height = ratioH2 * window.area.height;
        }

Ads DB Table

Some of the importang fields of the ads table:

Field Type Description
id Int
advertiser_id Int The id of the main advertiser
pdf_id Int The id of the pdf the ad was cropped from
article_id Int The id of the article this ad belongs to
alias String The alias of the article, used so we can generate it’s own page
image_id Int The id the of the actual image of the ad
width Int The ads width in pixels
height Int the ads height in pixels
width_mm Int Ad width in milimeters. See above section for calculation details
height_mm Int Ad height in milimeters. See above section for calculation details
col_num Int The number of columns the ad is taking on the pdf
start_date Date (Y-m-d) From which date the ad is supposed to be visible
end_date Date (Y-m-d) To Which date the ad is supposed to be visible
lat String Geo Latitude of the ad’s address. The advertiser lat has higher priority and it’s being used
lng String Geo Longitude of the ad’s address. The advertiser lng has higher priority and it’s being used
crop_data Text (json-encoded) The crop dimensions, sizes and coordinates from which the ad was cropped
Advertisers Text (json-encoded) Holds the advertisers data for an article (if there’s more then 1)
Placement Tiny-Integer Defines how the ad should be displayed on the article’s page. 0=regular, 1=understicial, 2=floating_right

For full list of the table fields, please check the table in the database