Loading files
0e185510cThis file is pinned to the selected release. Download the original to open it in your design tool.
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*******************************************************************************
* Includes
******************************************************************************/
#include "gerberattribute.h"
#include "../types/angle.h"
#include "../types/uuid.h"
#include <QtCore>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
GerberAttribute::GerberAttribute() noexcept
: mType(Type::Invalid), mKey(), mValues() {
}
GerberAttribute::GerberAttribute(Type type, const QString& key,
const QStringList& values) noexcept
: mType(type), mKey(key), mValues(values) {
}
GerberAttribute::GerberAttribute(const GerberAttribute& other) noexcept
: mType(other.mType), mKey(other.mKey), mValues(other.mValues) {
}
GerberAttribute::~GerberAttribute() noexcept {
}
/*******************************************************************************
* General Methods
******************************************************************************/
QString GerberAttribute::toGerberString() const noexcept {
// Use G04 comments since some PCB fabricators fail to parse X2 attributes.
// Some day we might provide an option to use real X2 attributes. However,
// maybe this is not needed at all so let's do it only if it has clear
// advantages.
return "G04 #@! " % toString() % "*\n";
}
QString GerberAttribute::toExcellonString() const noexcept {
return "; #@! " % toString() % "\n";
}
/*******************************************************************************
* Operator Overloadings
******************************************************************************/
GerberAttribute& GerberAttribute::operator=(
const GerberAttribute& rhs) noexcept {
mType = rhs.mType;
mKey = rhs.mKey;
mValues = rhs.mValues;
return *this;
}
bool GerberAttribute::operator==(const GerberAttribute& rhs) const noexcept {
return (mType == rhs.mType) && (mKey == rhs.mKey) && (mValues == rhs.mValues);
}
/*******************************************************************************
* Private Methods
******************************************************************************/
QString GerberAttribute::toString() const noexcept {
QString s = "T";
bool strictAscii = true; // For maximum compatibility with crappy readers!
switch (mType) {
case Type::File: {
s += "F";
break;
}
case Type::Aperture: {
s += "A";
break;
}
case Type::Object: {
s += "O";
// ASCII is not sufficient for component values like μ or Ω, thus we
// allow unicode for such attributes. Note that such attributes should
// appear in Gerber X3 assembly files anyway (not in PCB data files),
// so only modern 8X3) readers will need to handle unicode.
strictAscii = false;
break;
}
case Type::Delete: {
s += "D";
break;
}
default: {
return QString();
}
}
s += mKey;
foreach (const QString& value, mValues) {
s += "," + escapeValue(value, strictAscii);
}
return s;
}
/*******************************************************************************
* Static Methods
******************************************************************************/
GerberAttribute GerberAttribute::unset(const QString& key) noexcept {
return GerberAttribute(Type::Delete, key, {});
}
GerberAttribute GerberAttribute::fileGenerationSoftware(
const QString& vendor, const QString& application,
const QString& version) noexcept {
QStringList values = {vendor, application};
if (!version.isEmpty()) {
values.append(version);
}
return GerberAttribute(Type::File, ".GenerationSoftware", values);
}
GerberAttribute GerberAttribute::fileCreationDate(
const QDateTime& date) noexcept {
return GerberAttribute(Type::File, ".CreationDate",
{date.toString(Qt::ISODate)});
}
GerberAttribute GerberAttribute::fileProjectId(
const QString& name, const Uuid& uuid, const QString& revision) noexcept {
return GerberAttribute(Type::File, ".ProjectId",
{name, uuid.toStr(), revision});
}
GerberAttribute GerberAttribute::filePartSingle() noexcept {
return GerberAttribute(Type::File, ".Part", {"Single"});
}
GerberAttribute GerberAttribute::fileSameCoordinates(
const QString& identifier) noexcept {
QStringList values;
if (!identifier.isEmpty()) {
values.append(identifier);
}
return GerberAttribute(Type::File, ".SameCoordinates", values);
}
GerberAttribute GerberAttribute::fileFunctionProfile(bool plated) noexcept {
return GerberAttribute(Type::File, ".FileFunction",
{"Profile", plated ? "P" : "NP"});
}
GerberAttribute GerberAttribute::fileFunctionCopper(int layer,
CopperSide side) noexcept {
QStringList values = {"Copper", "L" % QString::number(layer)};
switch (side) {
case CopperSide::Top: {
values.append("Top");
break;
}
case CopperSide::Inner: {
values.append("Inr");
break;
}
case CopperSide::Bottom: {
values.append("Bot");
break;
}
default: {
qCritical()
<< "Unhandled switch-case in GerberAttribute::fileFunctionCopper():"
<< static_cast<int>(side);
return GerberAttribute();
}
}
return GerberAttribute(Type::File, ".FileFunction", values);
}
GerberAttribute GerberAttribute::fileFunctionSolderMask(
BoardSide side) noexcept {
switch (side) {
case BoardSide::Top: {
return GerberAttribute(Type::File, ".FileFunction",
{"Soldermask", "Top"});
}
case BoardSide::Bottom: {
return GerberAttribute(Type::File, ".FileFunction",
{"Soldermask", "Bot"});
}
default: {
qCritical() << "Unhandled switch-case in "
"GerberAttribute::fileFunctionSolderMask():"
<< static_cast<int>(side);
return GerberAttribute();
}
}
}
GerberAttribute GerberAttribute::fileFunctionLegend(BoardSide side) noexcept {
switch (side) {
case BoardSide::Top: {
return GerberAttribute(Type::File, ".FileFunction", {"Legend", "Top"});
}
case BoardSide::Bottom: {
return GerberAttribute(Type::File, ".FileFunction", {"Legend", "Bot"});
}
default: {
qCritical()
<< "Unhandled switch-case in GerberAttribute::fileFunctionLegend():"
<< static_cast<int>(side);
return GerberAttribute();
}
}
}
GerberAttribute GerberAttribute::fileFunctionPaste(BoardSide side) noexcept {
switch (side) {
case BoardSide::Top: {
return GerberAttribute(Type::File, ".FileFunction", {"Paste", "Top"});
}
case BoardSide::Bottom: {
return GerberAttribute(Type::File, ".FileFunction", {"Paste", "Bot"});
}
default: {
qCritical()
<< "Unhandled switch-case in GerberAttribute::fileFunctionPaste():"
<< static_cast<int>(side);
return GerberAttribute();
}
}
}
GerberAttribute GerberAttribute::fileFunctionGlue(BoardSide side) noexcept {
switch (side) {
case BoardSide::Top: {
return GerberAttribute(Type::File, ".FileFunction", {"Glue", "Top"});
}
case BoardSide::Bottom: {
return GerberAttribute(Type::File, ".FileFunction", {"Glue", "Bot"});
}
default: {
qCritical()
<< "Unhandled switch-case in GerberAttribute::fileFunctionGlue():"
<< static_cast<int>(side);
return GerberAttribute();
}
}
}
GerberAttribute GerberAttribute::fileFunctionPlatedThroughHole(
int fromLayer, int toLayer) noexcept {
return GerberAttribute(
Type::File, ".FileFunction",
{"Plated", QString::number(fromLayer), QString::number(toLayer), "PTH"});
}
GerberAttribute GerberAttribute::fileFunctionNonPlatedThroughHole(
int fromLayer, int toLayer) noexcept {
return GerberAttribute(Type::File, ".FileFunction",
{"NonPlated", QString::number(fromLayer),
QString::number(toLayer), "NPTH"});
}
GerberAttribute GerberAttribute::fileFunctionMixedPlating(
int fromLayer, int toLayer) noexcept {
// Note that "MixedPlating" is actually not an official Gerber attribute (yet)
// because Gerber specs say that NPTH and PTH must be separate files. However,
// some PCB fabricators require to send a single drill file with NPTH and PTH
// mixed (totally stupid), and in this case, Ucamco recommends to use the
// "FixedPlating" file function (not publicly documented, I guess).
return GerberAttribute(
Type::File, ".FileFunction",
{"MixedPlating", QString::number(fromLayer), QString::number(toLayer)});
}
GerberAttribute GerberAttribute::fileFunctionComponent(
int layer, BoardSide side) noexcept {
QString layerStr = "L" % QString::number(layer);
switch (side) {
case BoardSide::Top: {
return GerberAttribute(Type::File, ".FileFunction",
{"Component", layerStr, "Top"});
}
case BoardSide::Bottom: {
return GerberAttribute(Type::File, ".FileFunction",
{"Component", layerStr, "Bot"});
}
default: {
qCritical() << "Unhandled switch-case in "
"GerberAttribute::fileFunctionComponent():"
<< static_cast<int>(side);
return GerberAttribute();
}
}
}
GerberAttribute GerberAttribute::filePolarity(Polarity polarity) noexcept {
switch (polarity) {
case Polarity::Positive: {
return GerberAttribute(Type::File, ".FilePolarity", {"Positive"});
}
case Polarity::Negative: {
return GerberAttribute(Type::File, ".FilePolarity", {"Negative"});
}
default: {
qCritical() << "Unhandled switch-case in GerberAttribute::filePolarity():"
<< static_cast<int>(polarity);
return GerberAttribute();
}
}
}
GerberAttribute GerberAttribute::fileMd5(const QString& md5) noexcept {
return GerberAttribute(Type::File, ".MD5", {md5});
}
GerberAttribute GerberAttribute::apertureFunction(
ApertureFunction function) noexcept {
switch (function) {
case ApertureFunction::Profile: {
return GerberAttribute(Type::Aperture, ".AperFunction", {"Profile"});
}
case ApertureFunction::ViaDrill: {
return GerberAttribute(Type::Aperture, ".AperFunction", {"ViaDrill"});
}
case ApertureFunction::ComponentDrill: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"ComponentDrill"});
}
case ApertureFunction::ComponentDrillPressFit: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"ComponentDrill", "PressFit"});
}
case ApertureFunction::MechanicalDrill: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"MechanicalDrill"});
}
case ApertureFunction::Conductor: {
return GerberAttribute(Type::Aperture, ".AperFunction", {"Conductor"});
}
case ApertureFunction::NonConductor: {
return GerberAttribute(Type::Aperture, ".AperFunction", {"NonConductor"});
}
case ApertureFunction::ComponentPad: {
return GerberAttribute(Type::Aperture, ".AperFunction", {"ComponentPad"});
}
case ApertureFunction::SmdPadCopperDefined: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"SMDPad", "CuDef"});
}
case ApertureFunction::SmdPadSolderMaskDefined: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"SMDPad", "SMDef"});
}
case ApertureFunction::BgaPadCopperDefined: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"BGAPad", "CuDef"});
}
case ApertureFunction::BgaPadSolderMaskDefined: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"BGAPad", "SMDef"});
}
case ApertureFunction::ConnectorPad: {
return GerberAttribute(Type::Aperture, ".AperFunction", {"ConnectorPad"});
}
case ApertureFunction::HeatsinkPad: {
return GerberAttribute(Type::Aperture, ".AperFunction", {"HeatsinkPad"});
}
case ApertureFunction::ViaPad: {
return GerberAttribute(Type::Aperture, ".AperFunction", {"ViaPad"});
}
case ApertureFunction::TestPad: {
return GerberAttribute(Type::Aperture, ".AperFunction", {"TestPad"});
}
case ApertureFunction::FiducialPadLocal: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"FiducialPad", "Local"});
}
case ApertureFunction::FiducialPadGlobal: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"FiducialPad", "Global"});
}
case ApertureFunction::ComponentMain: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"ComponentMain"});
}
case ApertureFunction::ComponentPin: {
return GerberAttribute(Type::Aperture, ".AperFunction", {"ComponentPin"});
}
case ApertureFunction::ComponentOutlineBody: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"ComponentOutline", "Body"});
}
case ApertureFunction::ComponentOutlineCourtyard: {
return GerberAttribute(Type::Aperture, ".AperFunction",
{"ComponentOutline", "Courtyard"});
}
default: {
qCritical()
<< "Unhandled switch-case in GerberAttribute::apertureFunction():"
<< static_cast<int>(function);
return GerberAttribute();
}
}
}
GerberAttribute GerberAttribute::apertureFunctionMixedPlatingDrill(
bool plated, ApertureFunction function) noexcept {
// Note: This function shall only be used in mixed-plating Excellon files!
// See comment in fileFunctionMixedPlating() for details.
GerberAttribute a = apertureFunction(function);
if (plated) {
a.mValues.prepend("PTH");
a.mValues.prepend("Plated");
} else {
a.mValues.prepend("NPTH");
a.mValues.prepend("NonPlated");
}
return a;
}
GerberAttribute GerberAttribute::objectNet(const QString& net) noexcept {
return GerberAttribute(Type::Object, ".N", {net});
}
GerberAttribute GerberAttribute::objectComponent(
const QString& component) noexcept {
return GerberAttribute(Type::Object, ".C", {component});
}
GerberAttribute GerberAttribute::objectPin(const QString& component,
const QString& pin,
const QString& signal) noexcept {
QStringList values = {component, pin};
if (!signal.isEmpty()) {
values.append(signal);
}
return GerberAttribute(Type::Object, ".P", values);
}
GerberAttribute GerberAttribute::componentRotation(
const Angle& rotation) noexcept {
return GerberAttribute(Type::Object, ".CRot", {rotation.toDegString()});
}
GerberAttribute GerberAttribute::componentManufacturer(
const QString& manufacturer) noexcept {
return GerberAttribute(Type::Object, ".CMfr", {manufacturer});
}
GerberAttribute GerberAttribute::componentMpn(const QString& mpn) noexcept {
return GerberAttribute(Type::Object, ".CMPN", {mpn});
}
GerberAttribute GerberAttribute::componentValue(const QString& value) noexcept {
return GerberAttribute(Type::Object, ".CVal", {value});
}
GerberAttribute GerberAttribute::componentMountType(MountType type) noexcept {
switch (type) {
case MountType::Tht: {
return GerberAttribute(Type::Object, ".CMnt", {"TH"});
}
case MountType::Smt: {
return GerberAttribute(Type::Object, ".CMnt", {"SMD"});
}
case MountType::Fiducial: {
return GerberAttribute(Type::Object, ".CMnt", {"Fiducial"});
}
case MountType::Other: {
return GerberAttribute(Type::Object, ".CMnt", {"Other"});
}
default: {
qCritical()
<< "Unhandled switch-case in GerberAttribute::componentMountType():"
<< static_cast<int>(type);
return GerberAttribute();
}
}
}
GerberAttribute GerberAttribute::componentFootprint(
const QString& footprint) noexcept {
return GerberAttribute(Type::Object, ".CFtp", {footprint});
}
/*******************************************************************************
* Private Methods
******************************************************************************/
QString GerberAttribute::escapeValue(const QString& value,
bool strictAscii) noexcept {
QString ret = value;
// remove CRLF newlines
ret.remove('\r');
// replace newlines by spaces
ret.replace('\n', ' ');
if (strictAscii) {
// perform compatibility decomposition (NFKD)
ret = ret.normalized(QString::NormalizationForm_KD);
// remove all invalid characters for maximum compatibility with readers
QString validChars("-a-zA-Z0-9_+/!?<>\"'(){}.|&@# ;$:=");
ret.remove(QRegularExpression(QString("[^%1]").arg(validChars)));
} else {
// escape backslash
ret.replace("\\", "\\u005C");
// escape '%'
ret.replace("%", "\\u0025");
// escape '*'
ret.replace("*", "\\u002A");
// escape ','
ret.replace(",", "\\u002C");
}
// limit length to 65535 characters
ret.truncate(65535);
return ret;
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace librepcb