My Blog

CUPS and AirPrint with Brother DCP-7030, DCP-7055 and DCP-7065DN on FreeBSD, FreeNAS and possibly Raspberry Pi

by ebeaule on January 8, 2017 Comments Off on CUPS and AirPrint with Brother DCP-7030, DCP-7055 and DCP-7065DN on FreeBSD, FreeNAS and possibly Raspberry Pi

The Brother DCP-7030, DCP-7055 and DCP-7065DN are nice and cheap laser printers and, although they claim Windows, Mac and Linux compatibility, the linux compatibility has some fine prints. Brother provides binary drivers for RPM and Debian-based linux but no source code. If you are running linux on ARM, FreeBSD or any non-RPM or non-Debian-based linux, you will be out of luck with these binary drivers.

While there are some instructions out there to run Brother binary linux drivers in Linux compatibility mode under FreeBSD, this can be cumbersome to setup in a FreeNAS jail and will still not work on ARM-based setups.

I spent a day figuring out how to make my DCP-7065DN work with CUPS in a FreeNAS jail so that it could be published as an Airprint printer for my iOS devices. Here is how I got it to work (the same instructions are likely to work on any “nonstandard” linux setup).

Overview of the process

For this setup, we will need to:

  • Install CUPS and Avahi (Avahi is what allows Airprint to work)
  • Compile and install open source Brother drivers
  • Setup CUPS and Avahi/Airprint

Installing CUPS and Avahi

Follow your normal install procedure to install these. Under FreeBSD, the binary packages work fine. I ended up installing the following:

pkg install avahi-app avahi dbus avahi-dnsconfd cups

Note that DBUS is also needed for this setup to work (it wasn’t already installed in my FreeNAS Jail).

Once these are installed, configure your CUPS to your liking. On my setup, given that CUPS is only accessible from my local network, I removed most security using the following settings in /usr/local/etc/cups/cupsd.conf:

#
# Configuration file for the CUPS scheduler.  See "man cupsd.conf" for a
# complete description of this file.
#

# Log general information in error_log - change "warn" to "debug"
# for troubleshooting...
LogLevel warn
PageLogFormat

# Only listen for connections from the local machine.
#Listen localhost:631
#Allow remote access
Port 631
Listen /var/run/cups/cups.sock

# Show shared printers on the local network.
Browsing On
BrowseLocalProtocols dnssd

# Default authentication type, when authentication is required...
DefaultAuthType Basic

# Web interface setting...
WebInterface Yes

# Misc
FileDevice Yes
ServerAlias *
DefaultShared yes
DefaultEncryption Never

# Allow access to the server...
Order allow,deny
Allow all

Once CUPS is installed, make sure to enable it in /etc/rc.conf by adding the following (we’ll also enable DBUS and Avahi while we are at it):

cupsd_enable="YES"
avahi_daemon_enable="YES"
avahi_dnsconfd_enable="YES"
dbus_enable="YES"

You can then start CUPS like so:

services cupsd start

Once CUPS is started, point your browser to http://<localhost or your IP>:631 and confirm that everything is up and running.

We are now ready to compile the open source Brother print drivers…

Compiling and installing the open source Brother drivers

This is only needed if you cannot run the original Brother drivers (if you are running a RPM or Debian-based system, by all means, go with the official Brother drivers).

I found open source Brother drivers here: https://github.com/pdewacht/brlaser

Unfortunately, these did not compile under FreeBSD so I forked the code and made some tweaks to make them compile. The result is here: https://github.com/colddiver/brlaser

Follow the instructions on GitHub to compile and install the drivers.

Once installed, you will be ready to setup your printer and to configure AirPrint…

Setting up the Brother Printer and Airprint

I followed a guide available here: https://davidandrzejewski.com/2015/12/26/make-any-printer-airprint-compatible-on-freebsd/ and adapted some steps for my setup.

This can be done using the CUPS web interface as follows:

  1. Navigate to http://<localhost or your IP>:631 and click on the Administration menu
  2. Click Add Printer
  3. If your Brother printer is already on the network or connected to your CUPS server through USB, chances are that it will show up under the “Discovered Network Printers”. Select it if that’s the case. If not, use select the “lpd” option and type the IP address of your printer
  4. Make sure to check the “Share This Printer” box
  5. In the next screen, navigate to your Brother print drivers. The open source drivers will show up as <Printer Model> using brlaser v3
  6. Once setup, print a test page (from the printer’s “Maintenance” menu) to confirm that everything works.

If all went well, the test page should print OK.

Next, we need to configure CUPS with the AirPrint MIME types. You can do so by issuing the following commands (this is for FreeBSD – paths will change under a different distro):

echo "image/urf urf string(0,UNIRAST)" > /usr/local/share/cups/mime/airprint.types
echo "image/urf application/vnd.cups-postscript 66 pdftops" > /usr/local/share/cups/mime/airprint.convs
echo "image/urf application/pdf 100 pdftoraster" > /usr/local/share/cups/mime/airprint.convs

Restart CUPS (service cupsd restart).

We are now ready to setup Airprint… To do so, we will use a Python script to generate the necessary Avahi services. To use this script, I had to install Python PIP manually by issuing the following (this is assuming you already have Python installed – adjust to your version /usr/local/bin/python):

python2.7 -m ensurepip

We also need PyCUPS. This can be installed by issuing:

CPATH=/usr/local/include pip install pycups

Now we can download the airprint-generate Python script like so:

fetch https://raw.githubusercontent.com/tjfontaine/airprint-generate/master/airprint-generate.py

The original guide suggests that we need to modify the script like so:

In the section setting ‘DOCUMENT_TYPES’, add a new line just under “‘application/postscript’: True,” that says “‘application/vnd.cups-postscript’: True”.

Run the script by issuing :

python airprint-generate.py

This will create a .service file in the current directory for each of the printers you have set up in CUPS.  Put these service files into the Avahi service directory:

mv *.service /usr/local/etc/avahi/services

Finally, restart avahi-daemon and avahi-dnsconfd:

service avahi-daemon restart
service avahi-dnsconfd restart

At this point,  you should be able to AirPrint from your iOS devices.

read more
ebeauleCUPS and AirPrint with Brother DCP-7030, DCP-7055 and DCP-7065DN on FreeBSD, FreeNAS and possibly Raspberry Pi

ndhpple, libxml2, Alamofire and MagicalRecord in a Swift-based application (Xcode 6.4)

by ebeaule on May 13, 2016 Comments Off on ndhpple, libxml2, Alamofire and MagicalRecord in a Swift-based application (Xcode 6.4)

I had a need for a Swift project that would have to download (malformed) html file, parse them to extract relevant data and import said data into a Core Data model. The good news: there are excellent libraries/frameworks to make this simple:

  • NDHpple (a Swift port of Hpple), can do the parsing using XPath queries (requires libxml2)
  • Alamofire (Swift) can help with the networking stuff
  • MagicalRecord (Obj.-C) really can make things more pleasant on the Core Data side

The bad new, it is quite painful to setup the project to make them all work together. This post is about how to set the project so that everything will (mostly) work as it should. I’m writing this for my own benefit (so I can document how to do it again) and in the hope that it will help others…

Note that this tutorial was written for Xcode 6.4.

The Process

In a nutshell, the setup goes as follows:

  1. Create new project (don’t check Use Core Data)
  2. Install MagicalRecord through CocoaPods
  3. Install NDHpple
  4. Setup libxml2 in the project (build configuration – many steps there)
  5. Install Alamofire manually (it can be installed
  6. Setup Bridging-Headers.h for MagicalRecord (Shorthand) and make sure it won’t break libxml2

Detailed steps

I’ll be building on the excellent tutorial from James Leist and modifying as required…

1. Create the project

Start by creating a new Single View Application in Xcode. In the “Choose options for your new project” dialogue:

  • For the purpose of this tutorial, I called my application TestApp
  • select Swift for the programming language
  • make sure that “Use Core Data” is NOT selected. Since we’ll be using Core Data through MagicalRecord, we don’t need to have all the boilerplate Core Data code.

2. Install MagicalRecord with CocoaPods

CocoaPods is a dependency manager for Objective-C and Swift projects. You can find all kinds of useful information about it on its home page.

If you never installed it, open up a terminal window and type:

$ sudo gem install cocoapods

Now that it is installed, we’ll use CocoaPods to initialize or Podfile. The podfile is where we specify which libraries/framework we want to have managed on our project. To initialize the podfile, cd into the root of your project directory (same level as the .xcodeproj file) and run the following:

$ pod init

An empty podfile has been created in your project folder. You can add this to your Xcode project by dragging the file into your Xcode Project Navigator (this will enable you to edit the file straight from Xcode, which is convenient).

Open up the Podfile in Xcode and make sure it looks like this:

link_with  ['TestApp', 'TestAppTests']
# Uncomment this line to define a global platform for your project
platform :ios, '8.4'
# Installing MagicalRecord as a framework breaks it
#use_frameworks!
source 'https://github.com/CocoaPods/Specs.git'
pod 'MagicalRecord/Shorthand'
target 'TestApp' do
end
target 'TestAppTests' do
end

Make sure that the platform version matches with the “Deployment Target” of your project. You can confirm the deployment target by looking at your project settings like so:

DeployTarget

I added a commented out line for use_frameworks! to remember that this has to be explicitly disabled for MagicalRecord to work. It would be tempting to install Alamofire using CocoaPods as well but because Alamofire is in Swift, it will require the the use_frameworks! to be activated. Bottom line, you can’t install both Alamofire and MagicalRecord with CocoaPods (if someone knows how to do it without getting into various issues with MagicalRecord, let me know because I have not figured that one out yet). If/when MagicalFramework is converted to Swift, we’ll be able to revisit this part of the setup.

The source line tells CocoaPods where to grab the package info.

The pod line tells which pod we want to have installed by CocoaPods. Note the Shorthand part – this is important.

Now, we should be ready to install the package. First close your project in Xcode. Then, run the following in the terminal:

$ pod install

Be mindful of the instructions you got after running pod install:

[!] Please close any current Xcode sessions and use `TestApp.xcworkspace` for this project from now on.

So go ahead and close the project and re-open it using the TestApp.xcworkpace. Your project should now look like this:

MRInstalled

 

Notice that the podfile has been added under “Pods” (you can edit it from there now and remove the earlier reference).

We now need to add a bridging header to allow the Objective-C MagicalRecord code to be useable in our swift project. To do so, add a file your project by right-clicking on the TestApp folder in the Project Navigator like so:

AddingFile

Select the header file…

HeaderFile

… and call it TestApp-Bridging-Header.h (the naming convention for these file is <AppName>-Bridging-Header.h.

Your Bridging Header file would now be in your project like so:

BridgingHeaderFile

Edit TestApp-Bridging-Header.h to make it look like this:

//
//  TestApp-Bridging-Header.h
//  TestApp
//
//  Created by Étienne Beaulé on 2015-09-02.
//  Copyright (c) 2015 Étienne Beaulé. All rights reserved.
//

#define MR_SHORTHAND
#import <CoreData+MagicalRecord.h>

This will make MagicalRecord available to us but we need to make sure our project will use the Bridging Header file. To do so, go to the Project’s Build Settings and search for “bridg”. You should see the “Objective-C Bridging Header” setting. Double click on the empty field next to the label and enter TestApp/TestApp-Bridging-Header.h like so:

SettingHeader

By setting this up at the project level, it will apply to all targets (including debug and release), which is what we want.

At that point, we should be creating a data model writing some code to confirm that everything works as it should. I’ll follow the steps from James Leist’s tutorial here.

Let’s start by creating the data model. Right click on the main project folder and select “New file…” like we did before:

AddingFile

In this case, select a Core Data -> Data Model file like so:

CoreDataFile

In the next screen, name the model file after the application’s name (i.e. TestApp). You can use another name if you want but if you do so, MagicalRecord won’t automatically know what model file to use (no big deal, you can specify it and this is documented in MagicalRecord’s docs).

Now that we have a model file, we need to create some entities. Create the entities by clicking on the Add Entity button here:

CreatingEntities

Name the 2 entities as follows:

  1. ContainerEntity
  2. DetailEntity

Both entities should have a “name” attribute of type “String”. We must then create the relationships between the 2 entities like so:

  1. ContainerEntity needs a one-to-many relationship with DetailEntity. Name the relationship “detailEntities”
  2. DetailEntity should have a ont-to-one relationship with ContainerEntity. Name the relationship “containerEntity” and make sure to set the inverse relationship.

The following screenshots provide details and pointers as to where you can set this up for each entity.

ContainerEntity

DetailEntity

We also need to make sure that a proper class name is set for our entities. The class name must be in the following format: <EntityName>. To do so, click on “Default” in the Configurations section and enter the Class names as shown below:

SettingEntityClassNames

IMPORTANT: Be very careful with the above step. In some case, you will find recommendations to use a <AppName>.<EntityName> for the class name. This has never worked for me. Xcode seems to be picky about this and Xcode 7 apparently does it differently. If you experience errors like this when trying to create records in the app later on:

2015-09-02 10:41:33.240 TestApp[23765:1245743] CoreData: warning: Unable to load class named 'TestApp.ContainerEntity' for entity 'ContainerEntity'.  Class not found, using default NSManagedObject instead. Could not cast value of type 'NSManagedObject_ContainerEntity_' (0x7ff1217861a0) to 'TestApp.ContainerEntity' (0x10249ce60).

… it is most likely due to a mismatch between the Entity class name.

We should now be ready to generate the classes. To do so:

  1. Make sure your data model is selected
  2. Go to Xcode’s Editor Menu and select “Create NSManagedObject Subclass…”

CreateEntityClass1

On the next screen, make sure your app’s data model is selected like so:

CreateEntityClass2

On the next screen, select all your entities like so:

CreateEntityClass3

On the save dialog, make sure you select your project’s main folder for the “Group” and “Swift” as the language like so:

CreateEntityClass4

After you hit “Create”, your project should look like this:

CreateEntityClass5

We now need to edit the 2 entities to make sure that the Objective-C based MagicalRecord will be able to use them as documented here. To do so:

  1. Add @objc(ContainerEntity) just above the ContainerEntity class definition (class ContainerEntity: NSManagedObject {)
  2. Add @objc(DetailEntity) just above the DetailEntity class definition (class DetailEntity: NSManagedObject {)

Your classes should thus look like this:

//
//  ContainerEntity.swift
//  TestApp
//
//  Created by Étienne Beaulé on 2015-09-02.
//  Copyright (c) 2015 Étienne Beaulé. All rights reserved.
//

import Foundation
import CoreData

@objc(ContainerEntity)
class ContainerEntity: NSManagedObject {

    @NSManaged var name: String
    @NSManaged var detailEntities: NSSet

}

…and this:

//
//  DetailEntity.swift
//  TestApp
//
//  Created by Étienne Beaulé on 2015-09-02.
//  Copyright (c) 2015 Étienne Beaulé. All rights reserved.
//

import Foundation
import CoreData

@objc(DetailEntity)
class DetailEntity: NSManagedObject {

    @NSManaged var name: String
    @NSManaged var containerEntity: ContainerEntity

}

We’ll keep following James Leist’ tutorial at this point and create (not reproducing the steps here):

  1. ContainerEntityVC and the DetailEntityVC
  2. Storyboard

Once this is all done, everything should work and MagicalRecord should do it’s thing.

[to be continued]

read more
ebeaulendhpple, libxml2, Alamofire and MagicalRecord in a Swift-based application (Xcode 6.4)

Working on an iOS 8 version of MyMSC App

by ebeaule on September 11, 2014 No comments

I’ve been working on an iOS 8 version of MyMSC App for some time (since the early developer previews). I’m taking the opportunity to make several under the hood code optimizations (notably on the record page, which is currently crashing due to some updates on the mymsc.ca site). Be patient – I should have something within a couple of weeks!

read more
ebeauleWorking on an iOS 8 version of MyMSC App