package co.smartreceipts.android.workers.reports.pdf.renderer.text;
import android.content.Context;
import android.support.annotation.NonNull;
import com.google.common.base.Preconditions;
import com.tom_roush.pdfbox.pdmodel.PDDocument;
import com.tom_roush.pdfbox.util.awt.AWTColor;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import co.smartreceipts.android.utils.log.Logger;
import co.smartreceipts.android.workers.reports.pdf.utils.HeavyHandedReplaceIllegalCharacters;
import co.smartreceipts.android.workers.reports.pdf.utils.PdfBoxUtils;
import co.smartreceipts.android.workers.reports.pdf.fonts.PdfFontSpec;
import co.smartreceipts.android.workers.reports.pdf.pdfbox.PdfBoxWriter;
import co.smartreceipts.android.workers.reports.pdf.renderer.Renderer;
import co.smartreceipts.android.workers.reports.pdf.renderer.constraints.HeightConstraint;
import co.smartreceipts.android.workers.reports.pdf.renderer.constraints.WidthConstraint;
import co.smartreceipts.android.workers.reports.pdf.renderer.constraints.XPositionConstraint;
import co.smartreceipts.android.workers.reports.pdf.renderer.constraints.YPositionConstraint;
import co.smartreceipts.android.workers.reports.pdf.renderer.formatting.Alignment;
import co.smartreceipts.android.workers.reports.pdf.renderer.formatting.Color;
import co.smartreceipts.android.workers.reports.pdf.renderer.formatting.Font;
import co.smartreceipts.android.workers.reports.pdf.renderer.formatting.Padding;
import co.smartreceipts.android.workers.reports.pdf.misc.FixedWidthTextCell;
/**
* Renders text using a {@link PdfBoxWriter}
*/
public class TextRenderer extends Renderer {
private final Context context;
private final PDDocument pdDocument;
private final String string;
private FallbackTextRenderer fallbackRendererFactory;
public TextRenderer(@NonNull Context context, @NonNull PDDocument pdDocument, @NonNull String string,
@NonNull AWTColor color, @NonNull PdfFontSpec fontSpec) {
this(context, pdDocument, string, new Color(color), new Font(fontSpec));
}
public TextRenderer(@NonNull Context context, @NonNull PDDocument pdDocument, @NonNull String string,
@NonNull Color color, @NonNull Font font) {
this.context = Preconditions.checkNotNull(context.getApplicationContext());
this.pdDocument = Preconditions.checkNotNull(pdDocument);
this.string = HeavyHandedReplaceIllegalCharacters.getSafeString(Preconditions.checkNotNull(string));
this.width = WRAP_CONTENT;
this.height = WRAP_CONTENT;
this.getRenderingFormatting().addFormatting(color);
this.getRenderingFormatting().addFormatting(font);
}
private TextRenderer(@NonNull Context context, @NonNull PDDocument pdDocument, @NonNull String string) {
this.context = Preconditions.checkNotNull(context.getApplicationContext());
this.pdDocument = Preconditions.checkNotNull(pdDocument);
this.string = Preconditions.checkNotNull(string);
this.width = WRAP_CONTENT;
this.height = WRAP_CONTENT;
}
@NonNull
@Override
public Renderer copy() {
final TextRenderer copy = new TextRenderer(this.context, this.pdDocument, this.string);
copy.height = this.height;
copy.width = this.width;
if (this.fallbackRendererFactory != null) {
copy.fallbackRendererFactory = (FallbackTextRenderer) this.fallbackRendererFactory.copy();
}
copy.getRenderingConstraints().setConstraints(this.getRenderingConstraints());
copy.getRenderingFormatting().setFormatting(this.getRenderingFormatting());
return copy;
}
@Override
public void measure() throws IOException {
final AWTColor color = Preconditions.checkNotNull(getRenderingFormatting().getFormatting(Color.class));
final PdfFontSpec fontSpec = Preconditions.checkNotNull(getRenderingFormatting().getFormatting(Font.class));
final float padding = getRenderingFormatting().getFormatting(Padding.class, 0f);
final Float heightConstraint = getRenderingConstraints().getConstraint(HeightConstraint.class);
Float widthConstraint = getRenderingConstraints().getConstraint(WidthConstraint.class);
Preconditions.checkArgument(heightConstraint == null, "Height constraints are currently unsupported");
try {
// Test to see if we can encode this string or not
fontSpec.getFont().encode(string);
final float measuredHeight;
final float measuredWidth;
final List<String> lines;
if (widthConstraint != null) {
lines = new FixedWidthTextCell(widthConstraint, padding, string, fontSpec, color).getLines();
measuredWidth = widthConstraint;
} else {
lines = Collections.singletonList(string);
measuredWidth = PdfBoxUtils.getStringWidth(string, fontSpec);
}
measuredHeight = lines.size() * PdfBoxUtils.getFontHeight(fontSpec) + 2 * padding;
this.width = measuredWidth;
this.height = measuredHeight;
} catch (IllegalArgumentException e) {
Logger.warn(this, "Failed to render natively. Failing back as an Android image.", e);
fallbackRendererFactory = new FallbackTextRenderer(context, pdDocument, string);
// Forward our constraints are formatting to the fallback
fallbackRendererFactory.getRenderingFormatting().setFormatting(getRenderingFormatting());
fallbackRendererFactory.getRenderingConstraints().setConstraints(getRenderingConstraints());
// Measure
fallbackRendererFactory.measure();
this.width = fallbackRendererFactory.getWidth();
this.height = fallbackRendererFactory.getHeight();
}
}
@Override
public void render(@NonNull PdfBoxWriter writer) throws IOException {
if (fallbackRendererFactory == null) {
final AWTColor color = Preconditions.checkNotNull(getRenderingFormatting().getFormatting(Color.class));
final PdfFontSpec fontSpec = Preconditions.checkNotNull(getRenderingFormatting().getFormatting(Font.class));
float x = Preconditions.checkNotNull(getRenderingConstraints().getConstraint(XPositionConstraint.class));
float y = Preconditions.checkNotNull(getRenderingConstraints().getConstraint(YPositionConstraint.class));
final float padding = getRenderingFormatting().getFormatting(Padding.class, 0f);
final Alignment.Type alignment = getRenderingFormatting().getFormatting(Alignment.class, Alignment.Type.Centered);
final List<String> lines = new FixedWidthTextCell(width, padding, string, fontSpec, color).getLines();
for (final String line : lines) {
final float stringWidth = PdfBoxUtils.getStringWidth(line, fontSpec);
final float dx;
if (alignment == Alignment.Type.Centered) {
dx = (width - stringWidth) / 2.0f;
} else {
dx = 0;
}
// Move our cursor down
y += PdfBoxUtils.getFontHeight(fontSpec);
// Write the text
writer.printText(line, fontSpec, color, x + dx, y);
}
} else {
// Forward our constraints are formatting to the fallback
fallbackRendererFactory.getRenderingFormatting().setFormatting(getRenderingFormatting());
fallbackRendererFactory.getRenderingConstraints().setConstraints(getRenderingConstraints());
// Render
fallbackRendererFactory.render(writer);
}
}
}